目录
十、property_tree
property_tree 是一个保存了多个属性值的树形数据结构,它可以用类似路径的简单方式访问任意节点的属性,而且每个节点都可以用类似 STL 的风格遍历子节点。property_tree 特别适用于应用程序的配置数据处理,它可以解析 XML、JSON、INI 和 INFO这 4 种格式的文本数据,使用它能够减轻开发配置管理的工作。
property_tree库的核心类是 basic_ptree,它有两个重要的内部类型定义:self_type和 value_type 。这两个类型定义意味着 basic_ptree使用了组合模式,它是由多个 basic_ptree 复合而成的集合体,就像一个 std::list <key,basic_ptree> 。
- self_type是basic_ptree模板实例化后自身的类型,它也是子节点的类型。
- value_type 是节点的数据结构,它是一个 std::pair,含有节点的属性名(first)和节点自身(second)。
同标准字符串类 basic_string 一样,通常我们不直接使用 basic_ptree ,而是使用预定义好的 typedef:ptree、 wptree 、 iptree 、 wiptree ,这些名字中的前缀 i 表示忽略大小写,前缀 w 表示支持宽字符。
(一)处理xml
1.写xml、修改xml
property_tree可以使用write_xml( )写入配置信息,它有两种重载形式,可以接收文件名或输入输出流。它的操作具有对称性,使用模板成员函数 put( )可以修改属性树的节点值,如果子节点不存在就相当于新增节点,如果子节点存在则就地修改节点值。 put( )通常不需指定模板参数,因为属性值的类型可以自动推导。如果要向属性树中加入同名的新节点,需要使用add( )函数。
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
void StudyWidgets::TestPropertyTree()
{
boost::property_tree::ptree write_pt;
write_pt.put("info", "student info");
write_pt.put("info.name", "Lisa");
write_pt.put("info.age", 22);
write_pt.put("info.subjects.subject", "Chinese");
write_pt.add("info.subjects.subject", "Math");//向属性树中添加新节点
write_pt.add("info.subjects.subject", "English");
write_pt.put("info.teacher", "Tanasha");
write_pt.put("info.message", "She know PI is 3.141592653");
boost::property_tree::write_xml("./config1.xml", write_pt);
}
<?xml version="1.0" encoding="utf-8"?>
<info>student info
<name>Lisa</name>
<age>22</age>
<subjects>
<subject>Chinese</subject>
<subject>Math</subject>
<subject>English</subject>
</subjects>
<teacher>Tanasha</teacher>
<message>She know PI is 3.141592653</message>
</info>
2.解析xml
要解析xml配置文件,我们首先要用 read_xml( )函数解析 XML 并初始化 ptree ,它有两种重载形式,可以接收文件名或输入输出流。
void read_xml(const std::string &filename, Ptree &pt, int flags = 0, const std::locale &loc = std::locale());
第三个参数flags是对xml中标签以外的文字进行操作的一个参数,它可以取值0、1、2、4,其中0表示不进行任何操作。
//xml_parser_flags.hpp static const int no_concat_text = 0x1; static const int no_comments = 0x2; static const int trim_whitespace = 0x4;
第四个参数locale对象,它规定了数据格式,如日期、换行、编码等等,一般可以默认,详见C++ 标准库的 locale 类用法_LEE的专栏-CSDN博客_c++ locale
3.get()通过路径访问节点
在初始化 ptree 后,我们可以用模板成员函数get ( ) 通过路径(默认使用点号"."作为路径分隔符)访问属性树内的节点,用模板参数指明获取属性的类型。ptree的 get ( )函数支持默认值的用法,在指定路径的同时可以指定默认值。如果属性树中不存在该属性,则使用默认值。由于默认值已经有类型信息,所以 get( )的返回值类型可以被自动推导出来,不必再加模板参数。
property_tree 默认使用点号分隔路径,虽然不太符合操作系统和 XML 的习惯,但用它来表示有层次的属性还是很自然的。但有的时候,属性名中会出现点号, property_tree 也允许我们使用其他分隔符,如“ / ”。
4.get_child()获得含有多个子节点的节点对象
对于有多个子节点的节点,我们就需要使用 get_child( )先获得子节点对象,再像使用标准容器一样用 begin( )和end( )遍历子节点。迭代器指向 ptree 的value_type,它的 second 成员是子节点自身,其值可以直接用 data( )或 get_value( )访问。
<?xml version="1.0" encoding="utf-8"?>
<info>student info
<!-- this is a note -->
<name id="1">Lisa</name>
<age>22</age>
<subjects>
<subject>Chinese</subject>
<subject>Math</subject>
<subject>English</subject>
</subjects>
<teacher language="English">Tanasha</teacher>
<message>
<![CDATA[10<100]]>
</message>
<NO.1>She knew the PI was 3.141592653</NO.1>
</info>
boost::property_tree::ptree pt;
boost::property_tree::read_xml("./config.xml", pt, 4);
//get()通过路径访问属性树内的节点
std::string name = pt.get<std::string>("info.name");
int age = pt.get<int>("info.age");
std::cout << "name:" << name << ",age:" << age << std::endl;//name:Lisa,age:22
//get()支持默认值
std::string sex = pt.get<std::string>("info.sex", "女");
std::cout << "sex:" << sex << std::endl; //sex:女
//get_child()获得含有多个子节点的节点对象
auto subjects = pt.get_child("info.subjects");
//遍历:方法一
for (auto& child : subjects)
{
std::cout << child.second.get_value<std::string>() << std::endl;
}
//方法二
for (auto item = subjects.begin(); item != subjects.end(); item++)
{
std::cout << item->second.data() << std::endl;
}
try {
//NO.1:She know PI is 3.141592653
std::cout << "NO.1:" << pt1.get<std::string>(boost::property_tree::ptree::path_type("info/NO.1", '/')) << std::endl;
}
catch (std::exception e)
{
std::cout << e.what() << std::endl;//
}
5.find()浅查找
我们也可通过find( )成员函数来查找某个节点的值,但是find( )只支持浅层次查找,不能实现深层次的查找。
//find()查找某个节点,浅层次,不支持深度查找
auto item = pt.find("info");
std::cout << "info:" << item->second.data() << std::endl;//student info
6.get_optional()
get()函数有三种形式,除了前面的基本形式和默认值形式,还有一种get_optional,它也是默认值形式的用法 , 但它使用了 optional 库来包装返回值 , 如果 get 失败则 optional对象是无效的。
auto teacher = pt.get_optional<std::string>("teacher");//应该是info.teacher
if (teacher.is_initialized())
{
std::cout << teacher.get() << std::endl;
}
else {
std::cout << "teacher not exist" << std::endl;//teacher not exist
}
7.获得注释及属性值
property_tree 本身没有实现 XML 解析器,而是使用非 Boost 的开源项目 rapidxml ,因此,property_tree 使用 rapidxml 将所有的 XML 节点转换为属性树对应的节点,节点的标签名是属性名,节点的内容是属性值。节点的属性保存在该节点的<xmlattr>下级节点,注释保存在<xmlcomment>中,节点的文本内容(如CDATA ) 保存在 <xmItext> 中。
boost::property_tree::ptree pt1;
boost::property_tree::read_xml("./config.xml", pt1);
try {
std::cout << "注释:" << pt1.get<std::string>("info.<xmlcomment>") << std::endl;
std::cout << "id:" << pt1.get<std::string>("info.name.<xmlattr>.id") << std::endl;
std::cout << "teacher language:" << pt1.get<std::string>("info.teacher.<xmlattr>.language") << std::endl;
std::cout << "CDATA:" << pt1.get<std::string>("info.message") << std::endl;
}
catch (std::exception e)
{
std::cout << e.what() << std::endl;
}
//注释: this is a note
//id:1
//teacher language:English
//CDATA:
// 10<100
(二)处理Json
JSON ( JavaScript Object Notation ) 也是一种常用的数据交换格式,它的结构简单紧凑,它类似XML但没有那么多的“繁文缛节”。
#include <boost/property_tree/json_parser.hpp>
(三)处理ini
INI格式是 Windows 操作系统下非常流行的一种配置数据的格式,它曾经被广泛应用,但它较 XML 和JSON有些不足:不支持多级层次,只有简单的key / value 形式,不能表达复杂的配置,所以其他格式的配置数据可能无法转换成 INI 格式。
#include <boost/property_tree/ini_parser.hpp>
(四)处理info
INFO格式是 property_tree 的专用格式,它比 XML简洁,其风格类似 JSON ,但 INFO 带有许多 C ++ 的特征,如 # include 语句、斜杠字符转义等,特别适合 C ++ 程序员使用。
#include <boost/property_tree/info_parser.hpp>