#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 포맷 사용을 위해 boostproperty_tree를 사용하기로 했습니다. 실제로 사용해본 결과 그 간편함이 XMLtinyXML 조합 때 보다 훨씬 쾌적했습니다.  

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.hpp52 줄 부분을 보시면 밑의 스샷과 같은 부분이 있습니다.

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

+ Recent posts