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>的结果出来,由外面判断是否获取到结果了。
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>> conv2. 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 ConfigParser018.{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 wstring042. std::wstring to_wstr(const std::string& str)043. {044. return m_conv.from_bytes(str);045. }046. 047. // convert wstring to UTF-8 string048. 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. //获取节点的某个属性值,默认为string087. 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. else101. return pair.second.get_optional<R>(XMLATTR_DOT + attrName);102. }103. 104. //获取value_type的某个属性值,默认为string105. optional<wstring> Attribute(const wptree::value_type& pair, const wstring& attrName)106. {107. return Attribute<wstring>(pair, attrName);108. }109. 110. //根据某个属性生成一个<string, ptree>的multimap111. 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和filename33. }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的帮助类以及中文解析问题的解决(转)的更多相关文章
- (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
boost.property_tree可以用来解析xml和json文件,我主要用它来解析xml文件,它内部封装了号称最快的xml解析器rapid_xml,其解析效率还是很好的.但是在使用过程中却发现各 ...
- 转载 VC轻松解析XML文件 - CMarkup类的使用方法
VC轻松解析XML文件 - CMarkup类的使用方法http://www.cctry.com/thread-3866-1-1.html VC解析XML文件的工具有很多,CMarkup, tinyXM ...
- 解析xml数据存入bean映射到数据库的 需求解决过程
解析xml数据存入bean映射到数据库的 需求解决过程2017年12月19日 15:18:57 守望dfdfdf 阅读数:419 标签: xmlbean 更多个人分类: 工作 问题编辑版权声明:本文为 ...
- 使用boost/property_tree进行XML操作
之前一直用tinyxml来进行XML文件操作,刚刚接触的一个测试项目是使用boost操作的,虽然不清楚这两者的差异,但boost使用起来还挺方便的,所以简单整理一些关于boost解析和创建XML文件的 ...
- 阶段3 1.Mybatis_03.自定义Mybatis框架_4.自定义mybatis的编码-解析XML的工具类介绍
导入xml操作的类和用到的相关包 创建util包,然后把提供好的XMLConfigBuilder.java文件复制3过来 复制过来,里面用到了很多dom4j的东西 打开pom.xml 输入depend ...
- Java解析XML文档(简单实例)——dom解析xml
一.前言 用Java解析XML文档,最常用的有两种方法:使用基于事件的XML简单API(Simple API for XML)称为SAX和基于树和节点的文档对象模型(Document Object ...
- Android之 解析XML文件(1)—— Pull解析
(以下文章基本照抄<第一行代码>) 解析XML文件有很多方法,这里主要讲Pull解析和SAX解析.这篇文章主要是讲Pull解析. 一.Pull解析参考代码 先上代码: private vo ...
- DEX文件解析--4、dex类的类型解析
一.前言 前几篇系列文章链接: DEX文件解析---1.dex文件头解析 DEX文件解析---2.Dex文件checksum(校验和)解析 DEX文件解析--3.dex文件 ...
- Android中的几种解析XML文件的类
Ø DOM解析 优点: 1.XML树在内存中完整存储,因此可以直接修改其数据和结构. 2.可以通过该解析器随时访问XML树中的任何一个节点. 3.DOM解析器的API在使用上也相对比较简单. 缺点:如 ...
随机推荐
- Java枚举的小知识点
enum 是jdk1.5引入的,使用它可以创建枚举类型,就像使用class创建类一样. enum关键字创建的枚举类型默认是java.lang.Enum(一个抽象类)的子类 用法1 常量 一般定义常量都 ...
- [转] MachingLearning中的距离相似性计算以及python实现
参考:https://blog.csdn.net/gamer_gyt/article/details/75165842#t16 https://blog.csdn.net/ymlgrss/artic ...
- 温故而知新--JavaScript书摘(二)
前言 毕业到入职腾讯已经差不多一年的时光了,接触了很多项目,也积累了很多实践经验,在处理问题的方式方法上有很大的提升.随着时间的增加,愈加发现基础知识的重要性,很多开发过程中遇到的问题都是由最基础的知 ...
- python的开发环境配置-Eclipse-PyDev插件安装
安装PyDev插件的两种安装方法: 1.百度搜索PyDev 2.4.0.zip,下载后解压,得到Plugins和Feature文件夹,复制两文件夹到Eclipse目录,覆盖即可. 插件的版本要对应py ...
- html----不常见标签
控制文字滚动 <!-- direction="right up down left" --> <!-- behavior:滚动方式(包括3个值:scroll.sl ...
- 2017-2018-2 20155309 南皓芯 Exp7 网络欺诈防范
实践内容 本实践的目标理解常用网络欺诈背后的原理,以提高防范意识,并提出具体防范方法.具体实践有 1,简单应用SET工具建立冒名网站 2.ettercap DNS spoof 3.结合应用两种技术,用 ...
- webservice restful一个小例子
需求 公司有一个产品,包括前台WEB界面和多个后台服务,各个服务都需要在前面界面中进行配置和控 制,以调整服务的行为.以前,配置文件都存放在数据库中,界面上修改配置后入库,并发送消息(Socket)通 ...
- WebApi的调用-2.后台调用
httpClient调用方式 namespace SOA.Common { //httpClient调用WebApi public class HttpClientHelper { public st ...
- oneinstack如何安装ssl证书和配置Let's Encrypt免费SSL证书教程汇总(转)
OneinStack包含以下组合:lnmp(Linux + Nginx+ MySQL+ PHP) LNMP安装SSL安全证书 部署HTTPS:https://www.gworg.com/ssl/309 ...
- hibernate查询竟然有6种方法
hibernate查询的6种方法 1.HQL查询 2.对象化查询Criteria方法 3.动态查询DetachedCriteria 4.例子查询 5.sql查询 6.命名查询 1.HQL查询 stat ...