boost解析xml文件

前面我们介绍了xml文件,今天我们试着用boost库来解析xml文件。我们将举两个例子来说明怎么使用。

来自boost官方的例子

先看xml文件的内容:

<debug>
    <filename>debug.log</filename>
    <modules>
        <module>Finance</module>
        <module>Admin</module>
        <module>HR</module>
    </modules>
    <level>2</level>
</debug>

我们再来看如何使用boost读取和保存xml文件。

// ----------------------------------------------------------------------------
// Copyright (C) 2002-2006 Marcin Kalicinski
//
// Distributed under the Boost Software License, Version 1.0. 
// (See accompanying file LICENSE_1_0.txt or copy at 
// http://www.boost.org/LICENSE_1_0.txt)
//
// For more information, see www.boost.org
// ----------------------------------------------------------------------------

#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>

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

void debug_settings::load(const std::string &filename)
{

    // Create empty property tree object
    using boost::property_tree::ptree;
    ptree pt;

    // Load XML file and put its contents in property tree. 
    // No namespace qualification is needed, because of Koenig 
    // lookup on the second argument. If reading fails, exception
    // is thrown.
    read_xml(filename, pt);

    // Get filename and store it in m_file variable. Note that 
    // we specify a path to the value using notation where keys 
    // are separated with dots (different separator may be used 
    // if keys themselves contain dots). If debug.filename key is 
    // not found, exception is thrown.
    m_file = pt.get<std::string>("debug.filename");

    // Get debug level and store it in m_level variable. This is 
    // another version of get method: if debug.level key is not 
    // found, it will return default value (specified by second 
    // parameter) instead of throwing. Type of the value extracted 
    // is determined by type of second parameter, so we can simply 
    // write get(...) instead of get<int>(...).
    m_level = pt.get("debug.level", 0);

    // Iterate over debug.modules section and store all found 
    // modules in m_modules set. get_child() function returns a 
    // reference to child at specified path; if there is no such 
    // child, it throws. Property tree iterator can be used in 
    // the same way as standard container iterator. Category 
    // is bidirectional_iterator.
    //BOOST_FOREACH(ptree::value_type &v, pt.get_child("debug.modules"))
    // m_modules.insert(v.second.data());

}

void debug_settings::save(const std::string &filename)
{

    // Create empty property tree object
    using boost::property_tree::ptree;
    ptree pt;

    // Put log filename in property tree
    pt.put("debug.filename", m_file);

    // Put debug level in property tree
    pt.put("debug.level", m_level);

    // Iterate over modules in set and put them in property
    // tree. Note that the add function places new key at the
    // end of list of keys. This is fine in most of the
    // situations. If you want to place item at some other
    // place (i.e. at front or somewhere in the middle),
    // this can be achieved using a combination of the insert
    // and put_value functions
    BOOST_FOREACH(const std::string &name, m_modules)
        pt.add("debug.modules.module", name);

    // Write property tree to XML file
    write_xml(filename, pt); //write_xml(cout,pt); //这个函数有重载. 可以用流 也可直接用文件名. 

}

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

解析:

load函数:

首先定义了解析树

using boost::property_tree::ptree;
    ptree pt;

然后读取xml文件
接下来三行代码,读取文件里的内容。
我们注意到:
上面的xml的根节点是debug。然后有三个节点:filename,modules,level。
其中modules是一个含有子节点的复合节点。
于是:
1.

    m_file = pt.get<std::string>("debug.filename");

读取filename。如读取失败,则抛出异常。
2.

m_level = pt.get("debug.level", 0);

获取level数,当然了我们也可以通过和前一句一样的语法获取m_level:

m_level = pt.get<int>("debug.level");

但是同样这句话一旦获取不到,就会抛出异常,如果我们想获取不到,返回一个默认值0呢?此时可以使用

m_level = pt.get("debug.level", 0);

来实现。其中最后返回值的类型通过默认值来推断,非常类似c++11的auto语法。
3.

BOOST_FOREACH(ptree::value_type &v, pt.get_child("debug.modules"))
        m_modules.insert(v.second.data());

由于modules是一个复合节点,我们可以通过循环遍历的方法访问节点的子节点。
BOOST_FOREACH类似c++11的for(auto& value: range)
循环遍历的第一句就是:<module>Finance</module>,而v.first==module,v.second==Finance,但是我们要通过data()来获取。
我们可以通过改变上述语句为下面语句验证我的推断:

BOOST_FOREACH(ptree::value_type &v, pt.get_child("debug.modules"))
    {
        std::cout << v.first<< " "<<v.second.data()<<std::endl;
        m_modules.insert(v.second.data());
    }

值得注意的是我测试的时候发现获取first加不加.data()都可以,但获取second必须加.data().

save函数

实际上是read的翻译版,只需将get换成put即可.我们只要按照变量对应的标签加即可。

另一个更复杂的例子

xml文件如下:

<debug name="debugname">
    <file name="debug.log"/>
    <modules type="internal">
        <module1>Finance_Internal</module1>
        <module2>Admin_Internal</module2>
        <module3>HR_Internal</module3>
    </modules>

    <modules type="external">
        <module>Finance_External</module>
        <module>Admin_External</module>
        <module>HR_External</module>  
    </modules>
</debug>

分析以上xml文件,我们会发现此刻带有了属性,还有深层嵌套。分析起来,稍复杂一些。前面我们讲过xml文件中属性其实可以看成子元素的形式。因此我们对debug遍历的时候,第一句应该是name="debugname",第二句是<file name="debug.log"/>
第三句是:

    <modules type="internal">
        <module1>Finance_Internal</module1>
        <module2>Admin_Internal</module2>
        <module3>HR_Internal</module3>
    </modules>
 第四句是:   
    <modules type="external">
        <module>Finance_External</module>
        <module>Admin_External</module>
        <module>HR_External</module>  
    </modules>

然后我们看代码:

#include <iostream>
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/foreach.hpp>

using namespace std;
using namespace boost::property_tree;

int main(void){
    ptree pt;
    read_xml("debug_settings2.xml", pt);

    //loop for every node under debug
    BOOST_FOREACH(ptree::value_type &v1, pt.get_child("debug"))
    {
        if (v1.first == "<xmlattr>")
        { //it's an attribute
            //read debug name="debugname"
            cout << "debug name=" << v1.second.get<string>("name") << endl;
        }
        else if (v1.first == "file")
        {
            //read file name="debug.log"
            cout << " file name=" << v1.second.get<string>("<xmlattr>.name") << endl;
        }
        else
        { // v1.first == "modules"
            //get module type
            cout << " module type:" << v1.second.get<string>("<xmlattr>.type") << endl;

            //loop for every node under modules
            BOOST_FOREACH(ptree::value_type &v2, v1.second)
            {
                if (v2.first == "<xmlattr>")
                {  //it's an attribute
                    //this can also get module type
                    cout << " module type again:" << v2.second.get<string>("type") << endl;
                }
                else
                {
                    //all the modules have the same structure, so just use data() function.
                    cout << " module name:" << v2.second.data() << endl;
                }

            }//end BOOST_FOREACH
        }
    }//end BOOST_FOREACH
}

注意:
对于属性来说,first指”<xmlattr>“,而不是“name”,v.second指的是name的具体值.

参考文献:

1.使用Boost property tree来解析带attribute的xml
2.http://www.boost.org/doc/libs/1_46_1/doc/html/boost_propertytree/tutorial.html

你可能感兴趣的:(xml)