boost.property_tree可以用来解析xml和json文件,我主要用它来解析xml文件,它内部封装了号称最快的xml解析器rapid_xml,其解析效率还是很好的。但是在使用过程中却发现各种不好用,归纳一下不好用的地方有这些:获取不存在的节点时就抛出异常 获取属性值时,要排除属性和注释节点,如果没注意这一点就会抛出异常,让人摸不着头脑。 内存模型有点怪。 默认不支持中文的解析。解析中文会乱码。

ptree获取子节点

  获取子节点接口原型为get_child(node_path),这个node_path从当前路径开始的全路径,父路径和子路径之间通过“.”连接,如“root.sub.child”。需要注意的是get_child获取的是第一个子节点,如果我们要获取子节点列表,则要用路径“root.sub”,这个路径可以获取child的列表。如果获取节点的路径不存在则会抛出异常,这时,如果不希望抛出异常则可以用get_xxx_optional接口,该接口返回一个optional<T>的结果出来,由外面判断是否获取到结果了。

1.//ptree的optional接口
2.auto item = root.get_child_optional('Root.Scenes');

  该接口返回的是一个optional<ptree>,外面还要判断该节点是否存在,optional对象通过bool操作符来判断该对象是否是无效值,通过指针访问

符'*'来访问该对象的实际内容。建议用optional接口访问xml节点。

1.//ptree的optional接口
2.auto item = root.get_child_optional('Root.Scenes');
3.if(item)
4.    cout<<'该节点存在'<<endl;

ptree的内存模型

  ptree维护了一个pair<string, ptree>的子节点列表,first指向的是该节点的TagName,second指向的才是ptree节点,因此在遍历ptree子节点时要注意迭代器的含义。

1.for (auto& data : root)
2.{
3.    for (auto& item : data.second) //列表元素为pair<string, ptree>,要用second继续遍历
4.    {
5.        cout<<item.first<<endl;
6.    }
7.}

  需要注意的是ptree.first可能是属性('<xmlattr>')也可能是注释('<xmlcomment>'),只有非注释类型的节点才能使用获取属性值、子节点等常用接口。

ptree获取属性值

  通过get<T>(attr_name)可以获取属性的值,如果想获取属性的整形值的话,可以用get<int>('Id'),返回一个整数值。有一点要注意如果ptree.first为'<xmlcomment>'时,是没有属性值的,可以通过data()来获取注释内容。如果这个ptree.first不为<xmlattr>时需要在属性名称前面加'<xmlcomment>.',即get<int>('<xmlcomment>.Id')才能正确获取属性值。可以看到获取属性值还是比较繁琐的,在后面要介绍的帮助类中可以简化属性值的获取。如果要获取节点的值则用get_value()接口,该接口用来获取节点的值,如节点:<Field>2</Field>通过get_value()就可以获取值'2'。

解析中文的问题

  ptree只能解析窄字符的xml文件,如果xml文件中含有unicode如中文字符,解析出来就是乱码。解析unicode要用wptree,该类的接口均支持宽字符并且接口和ptree保持一致。要支持中文解析仅仅wptree还不够,还需要一个unicode转换器的帮助,该转换器可以实现宽字符和窄字符的转换,宽窄的互相转换函数有很多实现,不过c++11中有更简单统一的方式实现款窄字符的转换。

c++11中宽窄字符的转换:

1.std::wstring_convert<std::codecvt<wchar_t,char,std::mbstate_t>> conv
2. 
3.(newstd::codecvt<wchar_t,char,std::mbstate_t>('CHS'));
4.//宽字符转为窄字符
5.string str = conv.to_bytes(L'你好');
6.//窄字符转为宽字符
7.string wstr = conv.from_bytes(str);

  boost.property_tree在解析含中文的xml文件时,需要先将该文件转换一下。

  boost解决方法:

01.#include 'boost/program_options/detail/utf8_codecvt_facet.hpp'
02.void ParseChn()
03.{
04.    std::wifstream f(fileName);
05.    std::locale utf8Locale(std::locale(), new boost::program_options::detail::utf8_codecvt_facet());
06.    f.imbue(utf8Locale); //先转换一下
07. 
08.    //用wptree去解析
09.    property_tree::wptree ptree;
10.    property_tree::read_xml(f, ptree);   
11.}

  这种方法有个缺点就是要引入boost的libboost_program_options库,该库有二十多M,仅仅是为了解决一个中文问题,却要搞得这么麻烦,有点得不偿失。好在c++11提供更简单的方式,用c++11可以这样:

01.void Init(const wstring& fileName, wptree& ptree)
02.{
03.    std::wifstream f(fileName);
04.    std::locale utf8Locale(std::locale(), new std::codecvt_utf8<wchar_t>);
05.    f.imbue(utf8Locale); //先转换一下
06. 
07.    //用wptree去解析
08.    property_tree::read_xml(f, ptree);
09.}

  用c++11就不需要再引入boost的libboost_program_options库了,很简单。

property_tree的帮助类

  property_tree的帮助类解决了前面提到的问题:

用c++11解决中文解析问题 简化属性的获取 增加一些操作接口,比如一些查找接口 避免抛出异常,全部返回optional<T>对象 隔离了底层繁琐的操作接口,提供统一、简洁的高层接口,使用更加方便。

  下面来看看这个帮助类是如何实现的吧:

001.#include<boost/property_tree/ptree.hpp>
002.#include<boost/property_tree/xml_parser.hpp>
003.using namespace boost;
004.using namespace boost::property_tree;
005. 
006.#include <map>
007.#include <vector>
008.#include <codecvt>
009.#include <locale>
010.using namespace std;
011. 
012.const wstring XMLATTR = L'<xmlattr>';
013.const wstring XMLCOMMENT = L'<xmlcomment>';
014.const wstring XMLATTR_DOT = L'<xmlattr>.';
015.const wstring XMLCOMMENT_DOT = L'<xmlcomment>.';
016. 
017.class ConfigParser
018.{
019.public:
020. 
021.    ConfigParser() : m_conv(new code_type('CHS'))
022.    {
023.         
024.    }
025. 
026.    ~ConfigParser()
027.    {
028.    }
029. 
030.    void Init(const wstring& fileName, wptree& ptree)
031.    {
032.        std::wifstream f(fileName);
033.        std::locale utf8Locale(std::locale(), new std::codecvt_utf8<wchar_t>);
034.        f.imbue(utf8Locale); //先转换一下
035.        wcout.imbue(std::locale('chs')); //初始化cout为中文输出格式
036. 
037.        //用wptree去解析
038.        property_tree::read_xml(f, ptree);
039.    }
040. 
041.    // convert UTF-8 string to wstring
042.    std::wstring to_wstr(const std::string& str)
043.    {
044.        return m_conv.from_bytes(str);
045.    }
046. 
047.    // convert wstring to UTF-8 string
048.    std::string to_str(const std::wstring& str)
049.    {
050.        return m_conv.to_bytes(str);
051.    }
052. 
053.    //获取子节点列表
054.    auto Descendants(const wptree& root, const wstring& key)->decltype(root.get_child_optional(key))
055.    {
056.        return root.get_child_optional(key);
057.    }
058. 
059.    //根据子节点属性获取子节点列表
060.    template<typename T>
061.    vector<wptree> GetChildsByAttr(const wptree& parant, const wstring& tagName, const wstring& attrName, const T& attrVal)
062.    {
063.        vector<wptree> v;
064. 
065.        for (auto& child : parant)
066.        {
067.            if (child.first != tagName)
068.                continue;
069. 
070.            auto attr = Attribute<T>(child, attrName);
071. 
072.            if (attr&&*attr == attrVal)
073.                v.push_back(child.second);
074.        }
075. 
076.        return v;
077.    }
078. 
079.    //获取节点的某个属性值
080.    template<typename R>
081.    optional<R> Attribute(const wptree& node, const wstring& attrName)
082.    {
083.        return node.get_optional<R>(XMLATTR_DOT + attrName);
084.    }
085. 
086.    //获取节点的某个属性值,默认为string
087.    optional<wstring> Attribute(const wptree& node, const wstring& attrName)
088.    {
089.        return Attribute<wstring>(node, attrName);
090.    }
091. 
092.    //获取value_type的某个属性值
093.    template<typename R>
094.    optional<R> Attribute(const wptree::value_type& pair, const wstring& attrName)
095.    {
096.        if (pair.first == XMLATTR)
097.            return pair.second.get_optional<R>(attrName);
098.        else if (pair.first == XMLCOMMENT)
099.            return optional<R>();
100.        else
101.            return pair.second.get_optional<R>(XMLATTR_DOT + attrName);
102.    }
103. 
104.    //获取value_type的某个属性值,默认为string
105.    optional<wstring> Attribute(const wptree::value_type& pair, const wstring& attrName)
106.    {
107.        return Attribute<wstring>(pair, attrName);
108.    }
109. 
110.    //根据某个属性生成一个<string, ptree>的multimap
111.    template<class F = std::function<bool(wstring&)>>
112.    multimap<wstring, wptree> MakeMapByAttr(const wptree& root, const wstring& key, const wstring& attrName, F predict = [](wstring& str){return true; })
113.    {
114.        multimap<wstring, wptree> resultMap;
115.        auto list = Descendants(root, key);
116.        if (!list)
117.            return resultMap;
118.         
119.        for (auto& item : *list)
120.        {
121.            auto attr = Attribute(item, attrName);
122.            if (attr&&predict(*attr))
123.                resultMap.insert(std::make_pair(*attr, item.second));
124.        }
125. 
126.        return resultMap;
127.    }
128. 
129.private:
130.    using code_type = std::codecvt<wchar_t, char, std::mbstate_t>;
131.    std::wstring_convert<code_type> m_conv;
132.};

  测试文件test.xml和测试代码:

01.<?xml version='1.0' encoding='UTF-8'?>
02.<Root Id='123456'>
03.    <Scenes>
04.        <!--注释说明1-->
05.        <Scene Name='测试1'>
06.            <!--注释说明11-->
07.            <DataSource>
08.                <!--注释说明111-->
09.                <Data>
10.                    <!--注释说明111-->
11.                    <Item Id='1' FileName='测试文件1' />
12.                </Data>
13.                <Data>
14.                    <Item Id='2' FileName='测试文件2' />
15.                    <Item Id='3' FileName='测试文件3' />
16.                </Data>
17.            </DataSource>
18.        </Scene>
19.        <!--注释说明1-->
20.        <Scene Name='测试2'>
21.            <DataSource>
22.                <Data>
23.                    <Item Id='4' FileName='测试文件4' />
24.                </Data>
25.                <Data>
26.                    <Item Id='5' FileName='测试文件5' />
27.                </Data>
28.            </DataSource>
29.        </Scene>
30.    </Scenes>
31.</Root>
01.void Test()
02.{
03.    wptree pt; pt.get_value()
04.    ConfigParser parser;
05.    parser.Init(L'test1.xml', pt); //解决中文问题,要转换为unicode解析
06. 
07.    auto scenes = parser.Descendants(pt, L'Root.Scenes'); //返回的是optional<wptree>
08.    if (!scenes)
09.        return;
10. 
11.    for (auto& scene : *scenes)
12.    {
13.        auto s = parser.Attribute(scene, L'Name'); //获取Name属性,返回的是optional<wstring>
14.        if (s)
15.        {
16.            wcout << *s << endl;
17.        }
18. 
19.        auto dataList = parser.Descendants(scene.second, L'DataSource'); //获取第一个子节点
20.        if (!dataList)
21.            continue;
22. 
23.        for (auto& data : *dataList)
24.        {
25.            for (auto& item : data.second)
26.            {
27.                auto id = parser.Attribute<int>(item, L'Id');
28.                auto fileName = parser.Attribute(item, L'FileName');
29. 
30.                if (id)
31.                {
32.                    wcout << *id << L' ' << *fileName << endl; //打印id和filename
33.                }
34.            }
35.        }
36.    }
37.}

测试结果:

  可以看到通过帮助类,无需使用原生接口就可以很方便的实现节点的访问与操作。使用者不必关注内部细节,根据统一而简洁的接口就可以操作xml文件了。

  一点题外话,基于这个帮助类再结合linq to object可以轻松的实现linq to xml:

01.//获取子节点SubNode的属性ID的值为0x10000D的项并打印出该项的Type属性
02.from(node.Descendants('Root.SubNode')).where([](XNode& node)
03.{
04.    auto s = node.Attribute('ID');
05.    return s&&*s == '0x10000D';
06.}).for_each([](XNode& node)
07.{
08.    auto s = node.Attribute('Type');
09.    if (s)
10.        cout << *s << endl;
11.});

boost.property_tree解析xml的帮助类以及中文解析问题的解决(转)的更多相关文章

  1. (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决

    boost.property_tree可以用来解析xml和json文件,我主要用它来解析xml文件,它内部封装了号称最快的xml解析器rapid_xml,其解析效率还是很好的.但是在使用过程中却发现各 ...

  2. 转载 VC轻松解析XML文件 - CMarkup类的使用方法

    VC轻松解析XML文件 - CMarkup类的使用方法http://www.cctry.com/thread-3866-1-1.html VC解析XML文件的工具有很多,CMarkup, tinyXM ...

  3. 解析xml数据存入bean映射到数据库的 需求解决过程

    解析xml数据存入bean映射到数据库的 需求解决过程2017年12月19日 15:18:57 守望dfdfdf 阅读数:419 标签: xmlbean 更多个人分类: 工作 问题编辑版权声明:本文为 ...

  4. 使用boost/property_tree进行XML操作

    之前一直用tinyxml来进行XML文件操作,刚刚接触的一个测试项目是使用boost操作的,虽然不清楚这两者的差异,但boost使用起来还挺方便的,所以简单整理一些关于boost解析和创建XML文件的 ...

  5. 阶段3 1.Mybatis_03.自定义Mybatis框架_4.自定义mybatis的编码-解析XML的工具类介绍

    导入xml操作的类和用到的相关包 创建util包,然后把提供好的XMLConfigBuilder.java文件复制3过来 复制过来,里面用到了很多dom4j的东西 打开pom.xml 输入depend ...

  6. Java解析XML文档(简单实例)——dom解析xml

      一.前言 用Java解析XML文档,最常用的有两种方法:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object ...

  7. Android之 解析XML文件(1)—— Pull解析

    (以下文章基本照抄<第一行代码>) 解析XML文件有很多方法,这里主要讲Pull解析和SAX解析.这篇文章主要是讲Pull解析. 一.Pull解析参考代码 先上代码: private vo ...

  8. DEX文件解析--4、dex类的类型解析

    一.前言   前几篇系列文章链接:     DEX文件解析---1.dex文件头解析     DEX文件解析---2.Dex文件checksum(校验和)解析     DEX文件解析--3.dex文件 ...

  9. Android中的几种解析XML文件的类

    Ø DOM解析 优点: 1.XML树在内存中完整存储,因此可以直接修改其数据和结构. 2.可以通过该解析器随时访问XML树中的任何一个节点. 3.DOM解析器的API在使用上也相对比较简单. 缺点:如 ...

随机推荐

  1. TestNG测试方法

    @Test(enabled = false)有助于禁用此测试用例. 分组测试是TestNG中的一个新的创新功能,使用<groups>标记在testng.xml文件中指定分组. 它可以在&l ...

  2. 【ES】学习4-结构化搜索

    1. 结构化搜索得到的结果只有是和否,没有相似概念. term可以实现精确值查询 curl -XGET 'localhost:9200/logstash-cowrie/_search?pretty' ...

  3. bzoj2152 树分治

    还是太菜了,自己写的wa,但是找不到哪里错了,, 感觉现在学树分治早了点..以后回来再看吧 /* 多少点对之间的路径是3的倍数 */ #include<iostream> #include ...

  4. 性能测试三:jmeter进阶之图形插件

    一.图形化插件的使用 使用Jmeter插件可以更直观的查看tps和响应时间 插件官网: http://jmeter-plugins.org/downloads/all 第一种方法,找到需要的插件下载j ...

  5. 【C++ Primer 第13章】5. 动态内存管理类

    StrVec类的设计 [题目描述]:我们将实现标准库vector类的一个简化版本,我们所做的一个简化是不使用模板,我们类只用于string,因此,它被命名为StrVec. #include<io ...

  6. java -d

    DB_SERVER_URL="-Ddatasource.driver=oracle.jdbc.OracleDriver -Ddatasource.url=jdbc:oracle:thin:@ ...

  7. oracle中tables和views的区别

    tables存储的行和列的数据,可以做任何操作 views存储的是算法,是虚拟的数据

  8. 《Android进阶之光》--事件总线

    No1: EventBus三要素: 1)Event:事件 2)Subscriber:事件订阅者 3)Publisher:事件发布者 No2: EventBus的4种ThreadMode(线程模型): ...

  9. Golang vs PHP 之文件服务器

    前面的话 作者为golang脑残粉,本篇内容可能会引起phper不适,请慎读! 前两天有同事遇到一个问题,需要一个能支持上传.下载功能的HTTP服务器做一个数据中心.我刚好弄过,于是答应帮他搭一个. ...

  10. 谁记录了mysql error log中的超长信息(记pt-stalk一个bug的定位过程)

    [问题] 最近查看MySQL的error log文件时,发现有很多服务器的文件中有大量的如下日志,内容很长(大小在200K左右),从记录的内容看,并没有明显的异常信息. 有一台测试服务器也有类似的问题 ...