JSON 포맷 사용을 위해 boost의 property_tree를 사용하기로 했습니다. 실제로 사용해본 결과 그 간편함이 XML과 tinyXML 조합 때 보다 훨씬 쾌적했습니다.
위의 샘플 코드를 보시면 아시겠지만 굉장히 직관적입니다. 읽고, 가져다 쓰기만 하면 되죠. 타입도 대부분 알아서 정해줍니다. 다만, 배열이 조금 문제인데 이것이 무조건 문자열로만 읽는 것 같습니다. ( 해결법 아시는 분은 자비 점... )
문제는 좀더 있습니다. 특히 쓰기가 문제입니다. boost 1.46.1 버전 기준으로 property_tree를 이용해 JSON 파일 쓰기를 하면, 모든 값들이 문자열로만 저장이 됩니다. int 형이나 bool 형 상관없이 무조건 문자열로만 저장입니다. 읽기와는 전혀 딴판이죠. 그래서 구글링으로 해결책을 찾아보던 중 property_tree의 소스를 조금 수정 하는 것으로 이 문제를 해결할 수 있었습니다.
json_parser_write.hpp의 52 줄 부분을 보시면 밑의 스샷과 같은 부분이 있습니다.
중간에 제가 주석을 쳐놓은 부분이 있습니다. 이 부분이 모든 값을 " "로 감싸어 문자열로 만들어 버리는 부분입니다. 이 부분을 주석 처리하면 문자열이 아닌 값으로 저장이 되죠. 하지만 이렇게만 두면 문자열로 저장하고 싶은 부분도 " " 감싸짐 없이 그대로 저장되기때문에 파싱 할때 오류가 나게 됩니다.
문자열은 예외를 둬야 하죠. 다행히 propert_tree는 값을 지정할때 Translator를 지정할 수 있습니다. 이 기능을 이용해 문자열에만 " "를 감싸 줄 수 있습니다.
( 참고 : http://ko.wikipedia.org/wiki/%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C_A000~AFFF )
수정 한 후에는 wraite_json에 locale("kor") 옵션을 주고, 쓰기를 실행 합니다.
만세~
<!-- JSON 예제 파일 --> { "String": "테스트", "Number": 12345, "Boolen": true, "StrArray": [ "테스트1", "테스트2" ], "NumArray": [ 1, 2, 3, 4, 5 ], "SubValue" : { "SubString": "서브테스트", "SubNumber": 67890, "SubBoolen": false } }
// JSON 입력 테스트 EXPECT_TRUE( exists( "test.json" ) ); wptree pt; read_json( "test.json", pt, locale("kor") ); wstring strTest = pt.get<wstring>( L"String" ); EXPECT_TRUE( 0 == strTest.compare( L"테스트" ) ); int numTest = pt.get( L"Number", 0 ); EXPECT_TRUE( 12345 == numTest ); bool boolTest = pt.get( L"Boolen", false ); EXPECT_TRUE( boolTest ); vector<wstring> strArrayTest; BOOST_FOREACH( wptree::value_type &v, pt.get_child( L"StrArray" ) ) { strArrayTest.push_back( v.second.data() ); } EXPECT_TRUE( 2 == (int)strArrayTest.size() ); vector<wstring> numArrayTest; BOOST_FOREACH( wptree::value_type &v, pt.get_child( L"NumArray" ) ) { numArrayTest.push_back( v.second.data() ); } EXPECT_TRUE( 5 == (int)numArrayTest.size() ); wstring strSubTest = pt.get<wstring>( L"SubValue.SubString" ); EXPECT_TRUE( 0 == strSubTest.compare( L"서브테스트" ) ); int numSubTest = pt.get( L"SubValue.SubNumber", 0 ); EXPECT_TRUE( 67890 == numSubTest ); bool boolSubTest = pt.get( L"SubValue.SubBoolen", true ); EXPECT_TRUE( false == boolSubTest );
위의 샘플 코드를 보시면 아시겠지만 굉장히 직관적입니다. 읽고, 가져다 쓰기만 하면 되죠. 타입도 대부분 알아서 정해줍니다. 다만, 배열이 조금 문제인데 이것이 무조건 문자열로만 읽는 것 같습니다. ( 해결법 아시는 분은 자비 점... )
문제는 좀더 있습니다. 특히 쓰기가 문제입니다. boost 1.46.1 버전 기준으로 property_tree를 이용해 JSON 파일 쓰기를 하면, 모든 값들이 문자열로만 저장이 됩니다. int 형이나 bool 형 상관없이 무조건 문자열로만 저장입니다. 읽기와는 전혀 딴판이죠. 그래서 구글링으로 해결책을 찾아보던 중 property_tree의 소스를 조금 수정 하는 것으로 이 문제를 해결할 수 있었습니다.
json_parser_write.hpp의 52 줄 부분을 보시면 밑의 스샷과 같은 부분이 있습니다.
// Value or object or array if (indent > 0 && pt.empty()) { // Write value Str data = create_escapes(pt.template get_value <Str>()); //stream << Ch('"') << data << Ch('"'); stream << data; }
중간에 제가 주석을 쳐놓은 부분이 있습니다. 이 부분이 모든 값을 " "로 감싸어 문자열로 만들어 버리는 부분입니다. 이 부분을 주석 처리하면 문자열이 아닌 값으로 저장이 되죠. 하지만 이렇게만 두면 문자열로 저장하고 싶은 부분도 " " 감싸짐 없이 그대로 저장되기때문에 파싱 할때 오류가 나게 됩니다.
문자열은 예외를 둬야 하죠. 다행히 propert_tree는 값을 지정할때 Translator를 지정할 수 있습니다. 이 기능을 이용해 문자열에만 " "를 감싸 줄 수 있습니다.
template <typename T> struct tr { typedef T internal_type; typedef T external_type; boost::optional<T> get_value(const T &v) { return v.substr(1, v.size() - 2); } boost::optional<T> put_value(const T &v) { return L'"' + v + L'"'; } }; wptree wPt; wPt.put( L"StringTest", L"Test", tr<wstring>() ); wPt.put( L"NumTest", 1234 ); wPt.put( L"BoolenTest", false ); write_json( "writeTest.json", wPt );
<!-- 출력 결과물 --> { "StringTest": "Test", "NumTest": 1234, "BoolenTest": false }이제 문자열과 값이 구분되어 잘 나옵니다. 하지만 아직 한가지 문제가 더 남았습니다. 바로 한글!! property_tree는 기본적으로 한글 출력이 안됩니다. 읽는 거는 locale("kor") 옵션을 주고, 읽기가 가능하지만 쓰기에서는 locale("kor") 옵션을 준다고 해도 한글 출력이 안됩니다. json_parser_write.hpp 파일 내부를 보면 그 이유를 알수 있습니다.
// This assumes an ASCII superset. But so does everything in PTree. // We escape everything outside ASCII, because this code can't // handle high unicode characters. if (*b == 0x20 || *b == 0x21 || (*b >= 0x23 && *b <= 0x2E) || (*b >= 0x30 && *b <= 0x5B) || (*b >= 0x5D && *b <= 0xFF)) result += *b;보시면 문자열 범위에서 영어권 문자열표를 제외한 특수문자는 모조리 제외되어있습니다. 이 때문에 한글을 써도 제대로 출력이 안되는 것 입니다. 여기에 한글 문자열 범위를 추가해 줍니다.
( 참고 : http://ko.wikipedia.org/wiki/%EC%9C%A0%EB%8B%88%EC%BD%94%EB%93%9C_A000~AFFF )
수정 한 후에는 wraite_json에 locale("kor") 옵션을 주고, 쓰기를 실행 합니다.
template <typename T> struct tr { typedef T internal_type; typedef T external_type; boost::optional<T> get_value(const T &v) { return v.substr(1, v.size() - 2); } boost::optional<T> put_value(const T &v) { return L'"' + v + L'"'; } }; wptree wPt; wPt.put( L"StringTest", L"한글 테스트", tr<wstring>() ); wPt.put( L"NumTest", 1234 ); wPt.put( L"BoolenTest", false ); write_json( "writeTest.json", wPt, locale("kor") );
<!-- 출력 결과물 --> { "StringTest": "한글 테스트", "NumTest": 1234, "BoolenTest": false }이제 한글도 잘 나오게 되었습니다.
만세~
기쁨의 세레모니 짤