前面我們介紹了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 filenameint m_level; // debug levelstd::set<std::string> m_modules; // modules where logging is enabledvoid load(const std::string &filename);void save(const std::string &filename);
};void debug_settings::load(const std::string &filename)
{// Create empty property tree objectusing 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 objectusing boost::property_tree::ptree;ptree pt;// Put log filename in property treept.put("debug.filename", m_file);// Put debug level in property treept.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 functionsBOOST_FOREACH(const std::string &name, m_modules)pt.add("debug.modules.module", name);// Write property tree to XML filewrite_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 debugBOOST_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 typecout << " module type:" << v1.second.get<string>("<xmlattr>.type") << endl;//loop for every node under modulesBOOST_FOREACH(ptree::value_type &v2, v1.second){if (v2.first == "<xmlattr>"){ //it's an attribute//this can also get module typecout << " 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