boost.property_tree可以用来解析xml和json文件,我主要用它来解析xml文件,它内部封装了号称最快的xml解析器rapid_xml,其解析效率还是很好的。但是在使用过程中却发现各种不好用,归纳一下不好用的地方有这些:

  1. 获取不存在的节点时就抛出异常
  2. 获取属性值时,要排除属性和注释节点,如果没注意这一点就会抛出异常,让人摸不着头脑。
  3. 内存模型有点怪。
  4. 默认不支持中文的解析。解析中文会乱码。

ptree获取子节点

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

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

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

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

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

ptree的内存模型

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

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

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

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

std::wstring_convert<std::codecvt<wchar_t,char,std::mbstate_t>> conv

(newstd::codecvt<wchar_t,char,std::mbstate_t>("CHS"));
//宽字符转为窄字符
string str = conv.to_bytes(L"你好");
//窄字符转为宽字符
string wstr = conv.from_bytes(str);

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

  boost解决方法:

#include "boost/program_options/detail/utf8_codecvt_facet.hpp"
void ParseChn()
{
std::wifstream f(fileName);
std::locale utf8Locale(std::locale(), new boost::program_options::detail::utf8_codecvt_facet());
f.imbue(utf8Locale); //先转换一下 //用wptree去解析
property_tree::wptree ptree;
property_tree::read_xml(f, ptree);
}

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

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

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

  另外一种方法就是,仍然用ptree和string,只是在取出string字符串后,做一个转换为unicode的转换,就能得到中文字符串了。例如:

        auto child = item.second.get_child("Scenes.Scene");
auto oname = child.get_optional<string>("<xmlattr>.Name"); //oname内部存了一个unicode字符串,需要将其转换为宽字符串得到中文
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::wstring wide = converter.from_bytes(*oname); //宽字符串转为窄字符串
//std::string narrow = converter.to_bytes(L"foo");

property_tree的帮助类

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

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

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

#include<boost/property_tree/ptree.hpp>
#include<boost/property_tree/xml_parser.hpp>
using namespace boost;
using namespace boost::property_tree; #include <map>
#include <vector>
#include <codecvt>
#include <locale>
using namespace std; const wstring XMLATTR = L"<xmlattr>";
const wstring XMLCOMMENT = L"<xmlcomment>";
const wstring XMLATTR_DOT = L"<xmlattr>.";
const wstring XMLCOMMENT_DOT = L"<xmlcomment>."; class ConfigParser
{
public: ConfigParser() : m_conv(new code_type("CHS"))
{ } ~ConfigParser()
{
} void Init(const wstring& fileName, wptree& ptree)
{
std::wifstream f(fileName);
std::locale utf8Locale(std::locale(), new std::codecvt_utf8<wchar_t>);
f.imbue(utf8Locale); //先转换一下
wcout.imbue(std::locale("chs")); //初始化cout为中文输出格式 //用wptree去解析
property_tree::read_xml(f, ptree);
} // convert UTF-8 string to wstring
std::wstring to_wstr(const std::string& str)
{
return m_conv.from_bytes(str);
} // convert wstring to UTF-8 string
std::string to_str(const std::wstring& str)
{
return m_conv.to_bytes(str);
} //获取子节点列表
auto Descendants(const wptree& root, const wstring& key)->decltype(root.get_child_optional(key))
{
return root.get_child_optional(key);
} //根据子节点属性获取子节点列表
template<typename T>
vector<wptree> GetChildsByAttr(const wptree& parant, const wstring& tagName, const wstring& attrName, const T& attrVal)
{
vector<wptree> v; for (auto& child : parant)
{
if (child.first != tagName)
continue; auto attr = Attribute<T>(child, attrName); if (attr&&*attr == attrVal)
v.push_back(child.second);
} return v;
} //获取节点的某个属性值
template<typename R>
optional<R> Attribute(const wptree& node, const wstring& attrName)
{
return node.get_optional<R>(XMLATTR_DOT + attrName);
} //获取节点的某个属性值,默认为string
optional<wstring> Attribute(const wptree& node, const wstring& attrName)
{
return Attribute<wstring>(node, attrName);
} //获取value_type的某个属性值
template<typename R>
optional<R> Attribute(const wptree::value_type& pair, const wstring& attrName)
{
if (pair.first == XMLATTR)
return pair.second.get_optional<R>(attrName);
else if (pair.first == XMLCOMMENT)
return optional<R>();
else
return pair.second.get_optional<R>(XMLATTR_DOT + attrName);
} //获取value_type的某个属性值,默认为string
optional<wstring> Attribute(const wptree::value_type& pair, const wstring& attrName)
{
return Attribute<wstring>(pair, attrName);
} //根据某个属性生成一个<string, ptree>的multimap
template<class F = std::function<bool(wstring&)>>
multimap<wstring, wptree> MakeMapByAttr(const wptree& root, const wstring& key, const wstring& attrName, F predict = [](wstring& str){return true; })
{
multimap<wstring, wptree> resultMap;
auto list = Descendants(root, key);
if (!list)
return resultMap; for (auto& item : *list)
{
auto attr = Attribute(item, attrName);
if (attr&&predict(*attr))
resultMap.insert(std::make_pair(*attr, item.second));
} return resultMap;
} private:
using code_type = std::codecvt<wchar_t, char, std::mbstate_t>;
std::wstring_convert<code_type> m_conv;
};

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

<?xml version="1.0" encoding="UTF-8"?>
<Root Id="">
<Scenes>
<!--注释说明1-->
<Scene Name="测试1">
<!--注释说明11-->
<DataSource>
<!--注释说明111-->
<Data>
<!--注释说明111-->
<Item Id="" FileName="测试文件1" />
</Data>
<Data>
<Item Id="" FileName="测试文件2" />
<Item Id="" FileName="测试文件3" />
</Data>
</DataSource>
</Scene>
<!--注释说明1-->
<Scene Name="测试2">
<DataSource>
<Data>
<Item Id="" FileName="测试文件4" />
</Data>
<Data>
<Item Id="" FileName="测试文件5" />
</Data>
</DataSource>
</Scene>
</Scenes>
</Root>
void Test()
{
wptree pt;
ConfigParser parser;
parser.Init(L"test1.xml", pt); //解决中文问题,要转换为unicode解析 auto scenes = parser.Descendants(pt, L"Root.Scenes"); //返回的是optional<wptree>
if (!scenes)
return; for (auto& scene : *scenes)
{
auto s = parser.Attribute(scene, L"Name"); //获取Name属性,返回的是optional<wstring>
if (s)
{
wcout << *s << endl;
} auto dataList = parser.Descendants(scene.second, L"DataSource"); //获取第一个子节点
if (!dataList)
continue; for (auto& data : *dataList)
{
for (auto& item : data.second)
{
auto id = parser.Attribute<int>(item, L"Id");
auto fileName = parser.Attribute(item, L"FileName"); if (id)
{
wcout << *id << L" " << *fileName << endl; //打印id和filename
}
}
}
}
}

测试结果:

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

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

//获取子节点SubNode的属性ID的值为0x10000D的项并打印出该项的Type属性
from(node.Descendants("Root.SubNode")).where([](XNode& node)
{
auto s = node.Attribute("ID");
return s&&*s == "0x10000D";
}).for_each([](XNode& node)
{
auto s = node.Attribute("Type");
if (s)
cout << *s << endl;
});

  如果你觉得这篇文章对你有用,可以点一下推荐,谢谢。

  c++11 boost技术交流群:296561497,欢迎大家来交流技术。

(原创)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. UltraEdit编辑器|UE

    目前对我而言,还是比较喜欢editplus/notepad++. 注册码: UltraEdit-32 v15.00注册码:free userGGCDP-KIOGN-KQHLZ-RNCSK-KKKHZ- ...

  2. ios中要在tableview中添加事件的方法

    //触摸tableview背景响应事件. UIControl *bgcontrol = [[UIControl alloc]initWithFrame:self.tableView_typeList. ...

  3. 使用Thrift让Python为Java提供服务

    Thrift是基于TCP的,谷歌的GRPC是基于HTTP的.Thrift和GRPC都是比直接写个web接口进行调用更完美的方式,最明显的一点就是:我们可以定义结构体,避免了手动解析的过程. 但是,在将 ...

  4. 由m种数字组成的n位数有多少个

    知乎链接 问题描述 我和我女朋友的QQ号都是九位数字,这九个数字是有七个不同的数字组成的,我想问这种概率是多大,我们是不是特别我看缘分呢?求大神给算一下概率! 思路 定义问题:由7种数字组成的9位数一 ...

  5. UIAlertView/UIAlertController封装使用

    基于UIAlertView封装的JXTAlertView,这个是将之前写Demo时搞的一套快捷使用alertView的工具抽离整理出来的,并提供了C函数直接调用,像这样: jxt_showAlertT ...

  6. C语言学习笔记 (009) - 对函数的进一步讨论

    一.给main函数传参: # include <stdio.h> int main(int argc,char **argv){ printf(]); ; } 二.指向函数指针变量的定义 ...

  7. Linux IO系统分析(scsi篇)

    一.概述 Linux内核中SCSI子系统由SCSI上层,中间层,底层驱动模块三部分组成,负责管理SCSI资源和处理其他子系统,如文件系统,提交到SCSI子系统中的IO请求. 因此,理解SCSI子系统的 ...

  8. WCF入门教程(四)通过Host代码方式来承载服务 一个WCF使用TCP协议进行通协的例子 jquery ajax调用WCF,采用System.ServiceModel.WebHttpBinding System.ServiceModel.WSHttpBinding协议 学习WCF笔记之二 无废话WCF入门教程一[什么是WCF]

    WCF入门教程(四)通过Host代码方式来承载服务 Posted on 2014-05-15 13:03 停留的风 阅读(7681) 评论(0) 编辑 收藏 WCF入门教程(四)通过Host代码方式来 ...

  9. 【转载】抓包工具Fidder详解(主要来抓取Android中app的请求) 包括https

    文章:http://blog.csdn.net/jiangwei0910410003/article/details/19806999/ Fiddler官网:http://www.telerik.co ...

  10. Knockout: 使用CSS绑定和event的blur失去焦点事件, 给未通过校验的输入框添加红色边框突出显示.

    目的:使用knockout提供的机制实现输入框失去焦点后的校验工作,并使用CSS绑定给未通过校验的输入框添加红色边框突出显示. 步骤: 先在htm中添加.error的css样式, 并在输入框中的dat ...