#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>
xml일 경우
boost 쓰실때 포함해야 할 헤더 입니다. json_parser.hpp 를 xml_parser.hpp로 고치시면 됩니다.
아래 글 출처 http://kindtis.tistory.com/358
JSON 포맷 사용을 위해 boost의 property_tree를 사용하기로 했습니다. 실제로 사용해본 결과 그 간편함이 XML과 tinyXML 조합 때 보다 훨씬 쾌적했습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!-- JSON 예제 파일 --> { "String": "테스트", "Number": 12345, "Boolen": true, "StrArray": [ "테스트1", "테스트2" ], "NumArray": [ 1, 2, 3, 4, 5 ], "SubValue" : { "SubString": "서브테스트", "SubNumber": 67890, "SubBoolen": false } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | // 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 줄 부분을 보시면 밑의 스샷과 같은 부분이 있습니다.
1 2 3 4 5 6 7 8 | // 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를 지정할 수 있습니다. 이 기능을 이용해 문자열에만 " "를 감싸 줄 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 ); |
1 2 3 4 5 6 | <!-- 출력 결과물 --> { "StringTest": "Test", "NumTest": 1234, "BoolenTest": false } |
이제 문자열과 값이 구분되어 잘 나옵니다. 하지만 아직 한가지 문제가 더 남았습니다. 바로 한글!! property_tree는 기본적으로 한글 출력이 안됩니다. 읽는 거는 locale("kor") 옵션을 주고, 읽기가 가능하지만 쓰기에서는 locale("kor") 옵션을 준다고 해도 한글 출력이 안됩니다. json_parser_write.hpp 파일 내부를 보면 그 이유를 알수 있습니다.
1 2 3 4 5 6 | // 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") 옵션을 주고, 쓰기를 실행 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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" ) ); |
1 2 3 4 5 6 | <!-- 출력 결과물 --> { "StringTest": "한글 테스트", "NumTest": 1234, "BoolenTest": false } |
이제 한글도 잘 나오게 되었습니다.
만세~
'메타프로그래밍 > Boost::' 카테고리의 다른 글
c+11 과 boost 메모리풀 연동 (0) | 2015.03.19 |
---|---|
boost :: property_tree (0) | 2014.07.04 |
boost::intrusive_ptr (0) | 2013.05.13 |
boost::pool (0) | 2013.05.11 |
boost::dynamic_bitset (0) | 2013.03.08 |