前面我们介绍了xml文件,今天我们试着用boost库来解析xml文件。我们将举两个例子来说明怎么使用。
先看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;
}
解析:
首先定义了解析树
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().
实际上是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