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

读取数据

  1. 抛出异常版本

    ptree pt;
    /* ... */
    float v = pt.get<float>("a.path.to.float.value");
    

    根据参数path定位节点位置,读取数据,将其转换成float类型返回,如果失败则抛出异常:

    • 如果路径不存在,抛出ptree_bad_path异常
    • 如果值不能被转换,抛出ptree_bad_data
    • 它们都是从ptree_error派生而来,从而使通用处理成为可能
  2. 默认值版本

    ptree pt;
    /* ... */
    float v = pt.get("a.path.to.float.value", -1.f);
    

    执行与上面相同的操作,失败则返回第二个参数指定的默认值,不抛出异常。

    返回值类型由默认值决定,无需声明。

  3. 附加选项版本

    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将创建一个新节点来保存该值,即使指定的路径引用了现有节点。

参考资料

Chapter 31. Boost.PropertyTree