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

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>的结果出来,由外面判断是否获取到结果了。

view sourceprint?

1.//ptree的optional接口

2.auto item = root.get_child_optional(‘Root.Scenes‘);

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

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

view sourceprint?

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子节点时要注意迭代器的含义。

view sourceprint?

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中宽窄字符的转换:

view sourceprint?

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解决方法:

view sourceprint?

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可以这样:

view sourceprint?

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>对象 隔离了底层繁琐的操作接口,提供统一、简洁的高层接口,使用更加方便。

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

view sourceprint?

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和测试代码:

view sourceprint?

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>

view sourceprint?

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:

view sourceprint?

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.});

时间: 2024-08-01 14:34:52

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

第十五讲.数据解析(XML与JSON两种数据解析)

一.XML数据解析 1.SAX:Simple API for XML.基于事件驱动的解析方式,逐行解析数据.(采用协议回调机制) NSXMLParser的解析方法: 1 #import "ViewController.h" 2 #import "Model.h" 3 4 @interface ViewController ()<NSXMLParserDelegate> 5 6 @property(nonatomic,strong)UITableView

解析xml文件并且输出(SAX解析器)

本文解析三个xml文件,难度依次增加 文件1:p1.xml <?xml version="1.0" encoding="UTF-8" ?> <persons> <person> <name>张三</name> <age>22</age> </person> </persons> 文件2:p2.xml <?xml version="1.0&quo

解析xml字符串出现java.net.MalformedURLException: no protocol的解决办法

使用jdom解析xml字符串代码如下 String xml = "<root>......</root>"; SAXBuilder builder = new SAXBuilder(); Document doc = builder.build(xml); 运行时,抛异常java.net.MalformedURLException: no protocol...... 解决办法如下: String xml="<root>......<

dom4j解析Xml,dom4j解析带命名空间的Xml内容,dom4j解析xml为实体类

首先引入maven: <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>jaxen</groupId> <artifactId>jaxen</arti

DuiLib 源码分析之解析xml类CMarkup &amp; CMarkupNode 头文件

xml使用的还是比较多的,duilib界面也是通过xml配置实现的 duilib提供了CMarkkup和CMarkupNode类解析xml,使用起来也是比较方便的,比较好奇它是怎么实现的,如果自己来写一个 解析又需要怎样架构,架构之路还很遥远... 先来看看头文件吧,CMarkup主要是用于分割xml,判断xml格式是否正确:CMarkupNode主要是将CMarkup分割的xml,获取节点中的属性, 最多支持64个属性 1 enum 2 { 3 XMLFILE_ENCODING_UTF8 =

Android DOM、SAX、Pull解析XML(转)

本篇随笔将详细讲解如何在Android当中解析服务器端传过来的XML数据,这里将会介绍解析xml数据格式的三种方式,分别是DOM.SAX以及PULL. 一.DOM解析XML 我们首先来看看DOM(Document Object Model)这种方式解析xml,通过DOM解析xml在j2ee开发中非常的常见,它将整个xml看成是一个树状的结构,在解析的时候,会将整个xml文件加载到我们的内存当中,然后通过DOM提供的API来对我们的xml数据进行解析,这种方式解析xml非常的方便,并且我们可以通过

iOS解析XML数据

iOS中解析XML数据的类是  NSXMLParser,详细使用方法如下: 假设现在在内存中有XML的二进制数据对象(NSData):data(该数据可能来自网络,也可能是本地的文件数据),设置NSXMLParser对象的协议,代码如下: NSXMLParser *xmlParse = [[NSXMLParser alloc] initWithData:data]; [xmlParse setDelegate:self]; [xmlParse parse];// 解析开始 解析对象设置后,我们需

解析XML内容到User对象

users.xml 1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <xml-root> 4 <conn-params> 5 <conn-url>jdbc:mysql://192.168.101.7:3306/bbs</conn-url> 6 <conn-driver>com.mysql.jdbc.Driver</conn-driver> 7

Android网络下解析XML

XML(Extensible Markup Language)可拓展标记语言,它与HTML一样,都是SGML(标准通用标记语言),它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言. 它非常适合万维网传输,提供统一的方法来描述和交换独立于应用程序或供应商的结构化数据.在Android下有三种方式解析XML,分别为SAX.DOM.PULL:它们有各自的特点,在网络编程中会经常使用,根据实际情况选择哪一种解析方式. 1.内存占用 由于Android手机性能相对于PC还是