Currently, I find the documentation for boost::property_tree to be terrible. Hence, I hope this example will help others to work out how to use BOOST_FOREACH and recursion to traverse down the boost property tree. Here's how to create a class that loads and XML file to a property_tree, then display the hierarchy and then saves to another XML file.
#include <iostream>
#include <string>
#include <fstream>
#include <boost/foreach.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <locale>
using boost::property_tree::ptree;
using namespace std;
class configuration {
ptree pt;
public:
// constructor
configuration(const string& filename) {
load(filename); };
// internal property tree function
ptree load(const string& filename) {
return import_xml(filename); };
void save(const string& filename) {
export_xml(filename,pt); };
// import export property tree function
ptree property_tree(void) {
return pt; };
void display(void) {
display(0, pt); };
private:
ptree import_xml(const string& filename) {
ifstream input(filename.c_str());
read_xml(input, pt);
return pt; };
void export_xml(const string& filename, const ptree& pt) {
boost::property_tree::xml_writer_settings<char> w(' ',2);
write_xml(filename, pt, locale(), w); };
void display(const int depth, const ptree& tree) {
BOOST_FOREACH( ptree::value_type const&v, tree.get_child("") ) {
ptree subtree = v.second;
string nodestr = tree.get<string>(v.first);
// print current node
cout << string("").assign(depth*2,' ') << "* ";
cout << v.first;
if ( nodestr.length() > 0 )
cout << "=\"" << tree.get<string>(v.first) << "\"";
cout << endl;
// recursive go down the hierarchy
display(depth+1,subtree);
}
};
};
#define DEFAULT_CALORIES 0
int main(void)
{
configuration cfg("test_input.xml");
cfg.display();
cfg.save("test_output.xml");
// example to grab data from property tree
int calories = cfg.property_tree().get<int>("collection.recipe.nutrition.<xmlattr>.calories",DEFAULT_CALORIES);
cout << "Calories = " << calories << endl;
return 0;
}
Here is a sample test_input.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<collection>
<description>
Some recipes used for the XML tutorial.
</description>
<recipe>
<title>Beef Parmesan with Garlic Angel Hair Pasta</title>
<ingredient name="beef cube steak" amount="1.5" unit="pound"/>
...
<preparation>
<step>
Preheat oven to 350 degrees F (175 degrees C).
</step>
...
</preparation>
<comment>
Make the meat ahead of time, and refrigerate over night, the acid in the
tomato sauce will tenderize the meat even more. If you do this, save the
mozzarella till the last minute.
</comment>
<nutrition calories="1167" fat="23" carbohydrates="45" protein="32"/>
</recipe>
...
<!--another comment-->
</collection>
The standard output when applied to this file gives:
* collection="
...
"
* description="
Some recipes used for the XML tutorial.
"
* recipe="
...
"
* title="Beef Parmesan with Garlic Angel Hair Pasta"
* ingredient
* <xmlattr>
* name="beef cube steak"
* amount="1.5"
* unit="pound"
* preparation="
...
"
* step="
Preheat oven to 350 degrees F (175 degrees C).
"
* comment="
Make the meat ahead of time, and refrigerate over night, the acid in the
tomato sauce will tenderize the meat even more. If you do this, save the
mozzarella till the last minute.
"
* nutrition
* <xmlattr>
* calories="1167"
* fat="23"
* carbohydrates="45"
* protein="32"
* <xmlcomment>="another comment"
Notice that boost::property_tree create a separate child for XML
attributes and XML comments.