Chapter 31. Boost.PropertyTree
PropertyTree库可以为一些数据格式提供解析器和生成器,这些格式可以由这样的树表示,包括XML、INFO、INI和JSON。
简单介绍
PropertyTree库提供了一个数据结构,存储一个任意深度嵌套的value树,在每个级别上由某个key索引。 树的每个节点都存储自己的值,加上其子节点及其键的有序列表。 树允许通过路径轻松访问其任何节点,路径是多个键的级联。
PropertyTree是通用的数据结构,但特别适合保存配置数据。 树提供了自己的特定于树的接口,每个节点也是其子节点的STL兼容序列。
从概念上讲,一个节点可以被认为是以下结构:
struct ptree
{
data_type data; // data associated with the node
list< pair<key_type, ptree> > children; // ordered list of named children
};
快速使用
PropertyTree库可以为一些数据格式提供解析器和生成器,这些格式可以由这样的树表示,包括XML、INFO、INI和JSON。
使用一个XML文件来介绍用法:
<debug>
<filename>debug.log</filename>
<modules>
<module>Finance</module>
<module>Admin</module>
<module>HR</module>
</modules>
<level>2</level>
</debug>
首先需要include一些头文件:
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/foreach.hpp>
#include <string>
#include <set>
#include <exception>
#include <iostream>
namespace pt = boost::property_tree;
程序需要一个用来存储读取数据的类型,创建一个结构体:
struct debug_settings
{
std::string m_file; // log filename
int m_level; // debug level
std::set<std::string> m_modules; // modules where logging is enabled
void load(const std::string &filename);
void save(const std::string &filename);
};
编写load成员函数的定义:
void debug_settings::load(const std::string &filename)
{
// Create empty property tree object
pt::ptree tree;
// Parse the XML into the property tree.
pt::read_xml(filename, tree);
// Use the throwing version of get to find the debug filename.
// If the path cannot be resolved, an exception is thrown.
m_file = tree.get<std::string>("debug.filename");
// Use the default-value version of get to find the debug level.
// Note that the default value is used to deduce the target type.
m_level = tree.get("debug.level", 0);
// Use get_child to find the node containing the modules, and iterate over
// its children. If the path cannot be resolved, get_child throws.
// A C++11 for-range loop would also work.
BOOST_FOREACH(pt::ptree::value_type &v, tree.get_child("debug.modules")) {
// The data function is used to access the data stored in a node.
m_modules.insert(v.second.data());
}
}
编写save成员函数的定义:
void debug_settings::save(const std::string &filename)
{
// Create an empty property tree object.
pt::ptree tree;
// Put the simple values into the tree. The integer is automatically
// converted to a string. Note that the "debug" node is automatically
// created if it doesn't exist.
tree.put("debug.filename", m_file);
tree.put("debug.level", m_level);
// Add all the modules. Unlike put, which overwrites existing nodes, add
// adds a new node at the lowest level, so the "modules" node will have
// multiple "module" children.
BOOST_FOREACH(const std::string &name, m_modules)
tree.add("debug.modules.module", name);
// Write property tree to XML file
pt::write_xml(filename, tree);
}
main函数:
int main()
{
try
{
debug_settings ds;
ds.load("debug_settings.xml");
ds.save("debug_settings_out.xml");
std::cout << "Success\n";
}
catch (std::exception &e)
{
std::cout << "Error: " << e.what() << "\n";
}
return 0;
}
读取数据
抛出异常版本
ptree pt; /* ... */ float v = pt.get<float>("a.path.to.float.value");
根据参数path定位节点位置,读取数据,将其转换成float类型返回,如果失败则抛出异常:
- 如果路径不存在,抛出ptree_bad_path异常
- 如果值不能被转换,抛出ptree_bad_data
- 它们都是从ptree_error派生而来,从而使通用处理成为可能
默认值版本
ptree pt; /* ... */ float v = pt.get("a.path.to.float.value", -1.f);
执行与上面相同的操作,失败则返回第二个参数指定的默认值,不抛出异常。
返回值类型由默认值决定,无需声明。
附加选项版本
ptree pt; /* ... */ boost::optional<float> v = pt.get_optional<float>("a.path.to.float.value");
此版本使用boost::optional类处理提取失败,在成功提取时,它将返回由提取值初始化的boost::optional;否则,它将返回未初始化的boost::optional
path的分隔符默认是.
,要使用除默认之外的分隔符,需要显式构造一个path对象。路径类型是一个string_path实例化,因此引用它最简单的方法是::path_type
。
typedef ptree::path_type path;
pt.get<float>(path("p.a.t.h/t.o/v.a.l.u.e", '/'));
pt.get(path("p.a.t.h/t.o/v.a.l.u.e", '/'), 0, NULL);
pt.get_optional<std::string>(path("p.a.t.h/t.o/v.a.l.u.e", '/'));
添加数据
添加数据不需要处理路径为空的异常,因此如果提供的值不能转换为树的数据类型,函数将抛出。
添加数据会在路径遍历期间插入任何丢失的路径元素。
ptree pt;
pt.put("a.path.to.float.value", 3.14f);
// Overwrites the value
pt.put("a.path.to.float.value", 2.72f);
// Adds a second node with the new value.
pt.add("a.path.to.float.value", 3.14f);
put将覆盖现有值(如果有的话),而add将创建一个新节点来保存该值,即使指定的路径引用了现有节点。