본문 바로가기

C++ 에서 json 읽기/쓰기 (JsonCpp 라이브러리 이용)

1. JsonCpp

 

JsonCpp는 C++에서 .json 파일을 읽고 쓸 수 있게 하는 라이브러리 중 하나입니다. 속도가 느리지만 사용이 쉽다는 장점이 있어서인지 널리 사용되고 있습니다. (또 널리 쓰이는 C++의 json 라이브러리로 RapidJson이 있습니다. 속도가 아주 빠르다는 장점이 있습니다.) 

 

참고로 JsonCpp는 .json 파일을 읽었을 때 .json 파일 내에 순서대로 저장돼 있는 속성명 순서대로 멤버를 저장하지 않고 속성명의 알파벳 순서대로 멤버를 저장합니다. 속성명 순서가 중요한 json 파일을 읽을 때에는 다른 라이브러리(RapidJson 등)를 사용해야 합니다.

 

 

 

 

 

 

 

 

 

2. JsonCpp 설치(2021-09-19 기준, Visual Studio 2019 기준)

 

 

Visual Studio 2019가 제공하는 NuGet 패키지 관리자를 통해 아주 간단하게 JsonCpp 라이브러리를 설치해 사용할 수 있습니다.

 

 

1) Visual Studio 2019를 열고, 내가 만들고자 하는 프로젝트를 엽니다.

 

2) 상단바의 프로젝트(P) > NuGet 패키지 관리(N)... 을 누릅니다.

 

 

 

3) 찾아보기 탭에서 jsoncpp를 검색합니다.

 

세 번째 줄의 JsonCpp.Windows가 현재 정상적으로 작동하는 것으로 확인됩니다.

 

 

 

4) 설치 버튼을 누른 후 확인을 클릭하면 설치가 완료됩니다.

 

 

 

 

5) 프로젝트의 코드 소스파일 헤더에 #include <json/json.h> 를 추가합니다.

 

프로젝트의 코드 소스파일 헤더에 다음과 같이 씁니다.

 

#include <json/json.h>

 

이것으로써 C++에서 JsonCpp 라이브러리를 사용할 준비가 끝났습니다.

 

 

 

 

 

 

 

 

3. json 파일 이해

 

{
    "속성명1" : "abc",
    "속성명2" : [1, 2, 3],
    "속성명3" : {
                    "p1" : 0,
                    "p2" : 1
                },
    "속성명4" : [ {
                    "p1" : 0,
                    "p2" : 1
                  },
                  {
                    "p1" : 2,
                    "p2" : 3
                  } ]
}

 

json 파일은 위처럼 중괄호({})와 대괄호([]) 그리고 쉼표(,)와 콜론(:)으로 각각을 구분합니다.

 

하나의 중괄호쌍 안에 들어가는 것을 하나의 '객체'라 하며, 한 객체에는 수개의 멤버가 '속성명 : 속성값'의 형식으로, 쉼표(,)로 구분되어 저장돼 있습니다.

 

속성명은 항상 쌍따옴표에 묶인 문자열이며, 속성값에는 문자열(쌍따옴표로 묶인 것), 숫자, 배열(대괄호쌍으로 수개의 속성값을 묶은 것), 불리언(true/false), null이 들어갈 수 있습니다. 또한 속성값으로 다른 객체가 들어갈 수도 있습니다.

 

 

 

 

 

 

 

 

 

4. JsonCpp로 .json 파일 읽기

 

#include <fstream>
#include <json/json.h>

using namespace std;

int main()
{
    ifstream fin("in.json"); //.json 파일의 경로를 괄호 안에 씁니다.
    
    Json::Value root;
    fin >> root; //여기서의 >> 연산을 통해 in.json 파일의 내용이 root 변수에 저장됩니다.
    
    fin.close();
    return 0;
}

 

위와 같은 방식으로 Json::Value 형으로 선언된 변수에 .json 파일의 내용을 저장할 수 있습니다.

 

변수에 저장된 각 속성명에 해당하는 속성값은 다음과 같은 방식으로 호출할 수 있습니다. (변수명[속성명]의 형식으로 속성값을 호출합니다. 속성값이 배열인 경우, 0번 인덱스부터 차례로 [0], [1], ... 으로 각 속성값을 호출할 수 있습니다.) 다음은 위 3번의 .json 파일을 위와 같은 방식으로 root 변수에 저장했을 때의 사례입니다.

 

cout << root["속성명1"]; // 콘솔에 "abc"가 출력됩니다.

for (int i=0; i<3; i++)
    cout << root["속성명2"][i] << " "; // 콘솔에 "1 2 3 "이 출력됩니다.
    
cout << root["속성명3"]["p1"]; // 콘솔에 "0"이 출력됩니다.
cout << root["속성명4"][1]["p2"]; // 콘솔에 "3"이 출력됩니다.

 

 

 

 

 

 

 

 

5. Json::Value 형 변수 내용 변경하기

 

Json::Value 형 변수가 있을 때, '변수명[속성명] = 속성값;' 꼴로 쓰면 그 변수에 그 속성명 - 속성값 쌍이 멤버로 추가됩니다. (이미 있는 속성명을 쓰고 속성값을 달리 쓰면 그 속성명에 해당하는 속성값이 변경됩니다.)

 

root["속성명1"] = "abc";

 

'변수명[속성명][속성명(또는 배열의 인덱스명)] = 속성값;' 꼴로 쓰면 속성명에 지정하는 속성값으로 객체 또는 배열을 쓸 수 있습니다.

 

for (int i=0; i<3; i++)
    root["속성명2"][i] = i+1; //root["속성명2"].append(i+1); 로 써도 같은 결과가 나옵니다.
    
root["속성명3"]["p1"] = 0;
root["속성명3"]["p2"] = 1;
//Json::Value tmp; tmp["p1"] = 0; tmp["p2"] = 1; root["속성명3"] = tmp; 로 써도 같은 결과가 나옵니다.

 

.append() 함수는 속성명의 속성값으로 배열을 지정 및 배열의 원소를 추가하는 함수입니다. 또한 다음과 같이 .append() 함수를 통해 속성명에 지정된 배열의 원소로 객체를 추가할 수도 있습니다. 

 

for (int i=0; i<2; i++)
{
    Json::Value tmp;
    for (int j=1; j<=2; j++)
        tmp["p"+to_string(i)] = i*2+j-1;
    root["속성명4"].append(tmp);
}

 

이미 배열 아닌 속성값이 지정돼 있는 속성명에 대하여 .append() 함수를 사용할 수 없습니다. 예를 들어, 다음 구문은 정상적으로 작동하지 않습니다.

 

root["속성명2"] = 1;
root["속성명2"].append(2);

 

 

 

 

 

 

 

6. Json::Value 형 변수 내용을 .json 파일로 출력하기

 

다음과 같이 Json::Value형 변수를 파일스트림으로 출력할 수 있습니다.

 

Json::Value root;
root["속성명1"] = "abc";

...

ofstream fout("out.json"); //출력할 .json 파일의 이름과 경로를 괄호 안에 적습니다.

fout << root; //root의 내용이 out.json 파일에 출력됩니다.

fout.close();

 

 

 

 

 

 

 

 

 

 

 

* JsonCpp에서 한글로 출력하기(2021-09-19 기준)

 

JsonCpp는 한글로 된 .json 파일을 잘 읽고 코드 내부적으로도 잘 작동하지만, 한글을 파일스트림 또는 콘솔로 출력하면 정상적으로 출력이 되지 않습니다. 자체적으로 한글을 변환해서 사용하기 때문입니다.

 

NuGet으로는 JsonCpp의 한글을 다루는 소스코드를 수정하는 것이 불가능하기 때문에, 다음과 같이 다소 복잡한 과정을 거쳐야 비로소 JsonCpp에서 한글을 정상적으로 출력할 수 있습니다.

 

 

1) github에서 JsonCPP 다운받기

 

https://github.com/open-source-parsers/jsoncpp

 

GitHub - open-source-parsers/jsoncpp: A C++ library for interacting with JSON.

A C++ library for interacting with JSON. Contribute to open-source-parsers/jsoncpp development by creating an account on GitHub.

github.com

 

위 링크의 초록색 Code 버튼을 누르면 다음과 같은 윈도우가 뜹니다.

 

 

 

여기서 Download ZIP을 누르면 .zip 파일을 받을 수 있습니다. 다운받으면 압축을 풀어둡니다.

 

 

 

 

 

 

 

2) amalgamate.py 실행 후 dist 폴더에 있는 파일들을 프로젝트 파일이 있는 폴더로 이동

 

 

 

압축을 풀면 jsoncpp-master 폴더에서 amalgamate.py 파일을 발견할 수 있습니다. 이를 실행하면 jsoncpp-master 폴더에 dist라는 폴더가 생깁니다.

 

 

 

 

이 폴더 안에는 jsoncpp.cpp 파일이 있습니다. 이 파일을 내가 사용하고자 하는 Visual Studio 프로젝트 파일이 있는 폴더로 옮깁니다.

 

 

jsoncpp.cpp가 프로젝트 파일이 있는 폴더로 잘 옮겨진 모습입니다.

 

 

 

 

3) 프로젝트에 jsoncpp.cpp 파일 추가 후 열기

 

 

 

사용하고자 하는 프로젝트를 Visual Studio에서 연 후, 솔루션 탐색기의 소스파일 부분을 우클릭해 추가 > 기존 항목(G)... 을 클릭합니다.

 

 

 

여기서 jsoncpp.cpp 파일을 클릭한 후 추가 버튼을 누르면 jsoncpp.cpp 파일이 프로젝트에 추가됩니다. 이때 추가된 jsoncpp.cpp 파일을 더블클릭 하면 드디어 jsoncpp.cpp의 코드를 수정할 수 있습니다.

 

 

프로젝트에 jsoncpp.cpp 파일이 추가되었습니다. 더블클릭하면 내용을 수정할 수 있습니다.

 

 

 

 

4) static unsigned int utf8ToCodepoint(const char*& s, const char* e) 함수 내용 변경하기

 

Ctrl+F로 위 함수를 찾아간 후, 

 

  if (firstByte < 0x80)
      return firstByte;

 

이 if 구문 바로 밑에 다음과 같은 코드를 추가합니다.

 

  else return 7777; // 7777이 아니어도, 128-65535 사이 값이면 다 괜찮습니다.

 

* 이 코드는 utf8ToCodepoint() 함수의 뒷부분을 무력화하는 코드입니다.

 

 

5) static String valueToQuotedStringN(const char* value, size_t length, bool emitUTF8 = false) 함수 내용 변경하기

 

Ctrl+F로 위 함수를 찾아간 후, switch (*c) 구문 안의 default: 에서 다음 else if 구문을 찾습니다.

 

        ...
        else if (codepoint < 0x10000) {
          // Basic Multilingual Plane
          appendHex(result, codepoint);
        } else {
        ...

 

이 else if 바로 위에 다음과 같은 코드를 추가합니다.

 

        else if (codepoint == 7777) // 여기의 7777은 위 utf8ToCodepoint 함수를 고칠 때 쓴 숫자와 같아야 합니다.
            appendRaw(result, c[0]);

 

이처럼 jsoncpp.cpp의 코드를 변경하면 정상적으로 한글이 출력되는 것을 확인할 수 있습니다.

 

 

(한글 출력 부분은 https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=minkyuramer&logNo=221807330540 를 참조하였습니다.)