I am new with boost and xml and I am trying to scan an xml file and save some specific values.
I read this article and my question is: if the xml contains several <sked>, how do I iterate both of them?
maybe
BOOST_FOREACH ()
BOOST_FOREACH () // nested loop
lets say the given xml file as follow (the purpose is to save both IDs):
<?xml version="1.0"? encoding="utf-8"?>
<arrayOfSked>
<sked>
<ID> 1 </ID>
<version>2</version>
<flight>
<carrier>BA</carrier>
<number>4001</number>
<precision>0.1</precision>
</flight>
<flight cancelled="true">
<carrier>BA</carrier>
<number>4002</number>
<precision>0.2</precision>
</flight>
</sked>
<sked>
<ID> 2 </ID>
<version>2</version>
<flight>
<carrier>BA</carrier>
<number>4001</number>
<precision>0.1</precision>
</flight>
<flight cancelled="true">
<carrier>BA</carrier>
<number>4002</number>
<precision>0.2</precision>
</flight>
</sked>
</arrayOfSked>
Taking some inspiration from this older answer of mine (boost::ptree find? or how to access deep arrays? C++), write a function to visit the tree nodes:
template <typename Tree, typename Out>
Out enumerate_nodes(Tree const& pt, typename Tree::path_type path, Out out) {
if (path.empty())
return out;
if (path.single()) {
auto name = path.reduce();
for (auto& child : pt)
{
if (child.first == name)
*out++ = child.second;
}
} else {
auto head = path.reduce();
for (auto& child : pt) {
if (head == "*" || child.first == head) {
out = enumerate_nodes(child.second, path, out);
}
}
}
return out;
}
Now you can simply read the file:
using boost::property_tree::ptree;
ptree pt;
{
std::ifstream ifs("input.txt");
read_xml(ifs, pt);
}
And collect the flights:
std::vector<std::reference_wrapper<ptree const> > flights;
enumerate_nodes<ptree>(pt, "arrayOfSked.sked.flight", back_inserter(flights));
for (ptree const& flight : flights) {
std::cout << "Canceled:\t" << (flight.get("<xmlattr>.cancelled", false)?"yes":"no") << "\n";
std::cout << "Carrier:\t" << flight.get("carrier", "?") << "\n";
std::cout << "Number:\t" << flight.get("number", 0) << "\n";
std::cout << "Precision:\t" << flight.get("precision", 0.0) << "\n";
std::cout << "------------------------------\n";
}
Which prints:
Canceled: no
Carrier: BA
Number: 4001
Precision: 0.1
------------------------------
Canceled: yes
Carrier: BA
Number: 4002
Precision: 0.2
------------------------------
Canceled: no
Carrier: BA
Number: 4001
Precision: 0.1
------------------------------
Canceled: yes
Carrier: BA
Number: 4002
Precision: 0.2
------------------------------
Full Demo Online
Live On Coliru
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>
template <typename Tree, typename Out>
Out enumerate_nodes(Tree const& pt, typename Tree::path_type path, Out out) {
if (path.empty())
return out;
if (path.single()) {
auto name = path.reduce();
for (auto& child : pt)
{
if (child.first == name)
*out++ = child.second;
}
} else {
auto head = path.reduce();
for (auto& child : pt) {
if (head == "*" || child.first == head) {
out = enumerate_nodes(child.second, path, out);
}
}
}
return out;
}
int main() {
using boost::property_tree::ptree;
ptree pt;
{
std::ifstream ifs("input.txt");
read_xml(ifs, pt);
}
std::vector<std::reference_wrapper<ptree const> > flights;
enumerate_nodes<ptree>(pt, "arrayOfSked.sked.flight", back_inserter(flights));
for (ptree const& flight : flights) {
std::cout << "Canceled:\t" << (flight.get("<xmlattr>.cancelled", false)?"yes":"no") << "\n";
std::cout << "Carrier:\t" << flight.get("carrier", "?") << "\n";
std::cout << "Number:\t" << flight.get("number", 0) << "\n";
std::cout << "Precision:\t" << flight.get("precision", 0.0) << "\n";
std::cout << "------------------------------\n";
}
}
Related
I would like to know if there is a way to get the data in children nodes by iterating through a vector of the parent nodes. I have an XML file that I plan on changing frequently so hardcoding the attribute names is something I would like to avoid. Thus, I want to extract the data within my children nodes without stating the tag name of the nodes by using pt.get_child(myparentNodes). I have this in my main.
Any assistance is greatly appreciated!
vector<string> parentNodes;
ptree pt;
ifstream fileName("myxml");
read_xml(fileName, pt);
for(const ptree::value_type &parent : pt)
{
cout << parent.first << std::endl;
parentNodes.push_back(parent.first);
}
for(int i=0; i<parentNodes.size();i++)
{
BOOST_FOREACH(boost::property_tree::ptree::value_type const &node,pt.get_child(parentNodes[i]))
/* I'm having trouble properly accessing the children nodes here */
In your snippet (cleaned up a little):
std::vector<std::string> parentNodes;
for(auto const& parent : pt) {
std::cout << parent.first << std::endl;
parentNodes.push_back(parent.first);
}
appears to be collecting the names of tree nodes into parentNodes. However, this assumes that the names be unique, or non-empty.
Property names need not be unique, nor are they guaranteed to be non-empty. In fact arrays in Property Tree are frequently modeled as nodes with only unnamed child nodes.
Then you had trouble getting the children of corresponding nodes. Let's first do it the tedious way (again, assuming the names are unique):
for (size_t i = 0; i < parentNodes.size(); i++) {
auto& parent = pt.get_child(parentNodes[i]);
for (auto const& child : parent) {
std::cout << child.first << std::endl;
}
}
Of course using ranged-for is much easier:
for (auto const& name : parentNodes) {
auto& parent = pt.get_child(name);
for (auto const& child : parent) {
std::cout << child.first << std::endl;
}
}
Better Yet
You can avoid the assumptions about naming as well as the second loop and the vector storage:
for (auto const& parent : pt) {
std::cout << parent.first << std::endl;
auto& node = parent.second;
for (auto const& child : node) {
std::cout << child.first << std::endl;
}
}
This is because the iterator points to a pair of (key, value). In fact, on a recent compiler you can write the loop with structured bindings:
for (auto const& [name, node] : pt) {
std::cout << name << std::endl;
for (auto const& child : node) {
std::cout << child.first << std::endl;
}
}
still doing the same.
Generalizing
You said you want to make this generic. However, assuming two-layer hierarchy of parent/child relations does not strike me as "generic". I linked you to some examples that show generic traversal (e.g. looking for patterns throughout the whole tree) last time, e.g. Iterating on xml file with boost - A function from that example:
Live On Wandbox
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
static auto settings = boost::property_tree::xml_writer_make_settings<std::string>(' ', 4);
template <typename Out>
Out enumerate_nodes(ptree const& pt, ptree::path_type path, Out out) {
if (path.empty())
return out;
if (path.single()) {
auto name = path.reduce();
for (auto& child : pt) {
if (child.first == name)
*out++ = child.second;
}
} else {
auto head = path.reduce();
for (auto& child : pt) {
if (head == "*" || child.first == head) {
out = enumerate_nodes(child.second, path, out);
}
}
}
return out;
}
int main() {
std::ifstream fileName("input.xml");
ptree pt;
read_xml(fileName, pt);
for (auto const& [name, node] : pt) {
std::cout << name << std::endl;
for (auto const& child : node)
std::cout << child.first << std::endl;
}
std::vector<std::reference_wrapper<ptree const>> matched;
enumerate_nodes(pt, "root.parent2.child3", back_inserter(matched));
for (ptree const& match : matched)
std::cout << "Matched: " << match.get_value<std::string>() << "\n";
}
When using input.xml:
<?xml version="1.0"?>
<root>
<parent1>
<child1>parent1/child1</child1>
<child2>parent1/child2</child2>
<child3>parent1/child3</child3>
<child4>parent1/child4</child4>
</parent1>
<parent2>
<child1>parent2/child1</child1>
<child2>parent2/child2</child2>
<child3>parent2/child3</child3>
<child4>parent2/child4</child4>
</parent2>
<parent3>
<child1>parent3/child1</child1>
<child2>parent3/child2</child2>
<child3>parent3/child3</child3>
<child4>parent3/child4</child4>
</parent3>
<parent4>
<child1>parent4/child1</child1>
<child2>parent4/child2</child2>
<child3>parent4/child3</child3>
<child4>parent4/child4</child4>
</parent4>
</root>
Prints
root
parent1
parent2
parent3
parent4
Matched: parent2/child3
I'm trying to parse this type of string
1.2e3ex
1.2e3 ex
And have set up
x3::float_ >> "ex"
Unfortunately, this fails to parse
1ex
Full example code:
#include <iostream>
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;
const auto parser = x3::float_ >> "em";
int main()
{
std::string input = "1em";
auto first = input.begin();
auto last = input.end();
float value{};
bool result = x3::phrase_parse(first, last, parser, x3::blank, value);
if(result)
{
if(first == last)
std::cout << "parse succesful: " << value << '\n';
else
std::cout << "incomplete parse: " << value << '\n';
}
else
std::cout << "parse unsuccesful\n";
}
Available live on Coliru as well.
It seems I'd need to jump through some hoops,
struct non_scientific_float_policy : x3::real_policies<float>
{
template <typename Iterator>
static bool parse_exp(Iterator& first, Iterator const& last)
{
return false;
}
};
const auto non_scientific_float = x3::real_parser<float, non_scientific_float_policy>{};
and provide an alternative:
const auto parser = non_scientific_float >> "em" | x3::float_ >> "em";
Is there no other way?
You can solve the issue by tuning the real policy parse_exp in such way that exponent detection must expect not only [eE] character but [eE][-+]?[0-9].
#include <iostream>
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;
template <typename T>
struct alt_real_policies : x3::real_policies<T>
{
template <typename Iterator>
static bool parse_exp(Iterator& first, Iterator const& last)
{
Iterator save = first;
if (x3::real_policies<T>::parse_exp(first, last)) {
Iterator iter = first;
if (x3::extract_int<x3::unused_type, 10, 1, 1>::call(iter, last, x3::unused))
return true;
}
first = save;
return false;
}
};
const x3::real_parser<float, alt_real_policies<float>> altfloat;
const auto parser = altfloat >> "em";
int main()
{
std::string input = "1em";
auto first = input.begin();
auto last = input.end();
float value{};
bool result = x3::phrase_parse(first, last, parser, x3::blank, value);
if (result)
{
if (first == last)
std::cout << "parse succesful: " << value << '\n';
else
std::cout << "incomplete parse: " << value << '\n';
}
else
std::cout << "parse unsuccesful\n";
}
http://coliru.stacked-crooked.com/a/f60f334c960cb602
You can use an alternative policy for non-greedy parsing of the exponent. The very simplest I can think of is:
Live On Coliru
#include <boost/spirit/home/x3.hpp>
#include <iostream>
namespace x3 = boost::spirit::x3;
template <typename T>
struct no_exponent : x3::real_policies<T> {
template <typename It>
static bool parse_exp(It, It) { return false; }
};
x3::real_parser<double, no_exponent<double> > noexp_;
const auto parser = (x3::float_ | noexp_) >> "em";
int main() {
std::string input = "-1.67em";
auto first = input.begin();
auto last = input.end();
float value{};
bool result = x3::phrase_parse(first, last, parser, x3::blank, value);
if (result) {
if (first == last)
std::cout << "parse succesful: " << value << '\n';
else
std::cout << "incomplete parse: " << value << '\n';
} else
{
std::cout << "parse unsuccesful\n";
}
}
Printing:
parse succesful: -1.67
I have a small but complex tree structure. Using, boost property tree as a container i am trying to iterate through the tree and subsequently emit it to the yaml file using yaml-cpp library.
For instance, i have a small nested property tree:
fibonacci:
type: series
entities:
golden_ratio:
ratio: 2.3
function:
power_series: 2
I want my yaml file to look exactly like this.
I wrote a recursive function to iterate through the tree and emit to yaml.
//Member variable
YAML::Emitter m_out
void iterator(const boost::property_tree::ptree& tree, const std::string& key)
{
for (const auto& item: tree)
{
if (item.second.data().empty()) //check if map node
{
m_out << YAML::BeginMap;
m_out << YAML::Key << item.first;
}
else if (!item.second.data().empty()) //else it is key/value pair
{
m_out << YAML::Key << item.first;
m_out << YAML::Value << item.second.data();
}
if (!item.second.empty()) //If the node has child
{
iterator(item.second, item.first);
}
}
}
I am calling the function with a emtpy key as iterator(root, ""). I know that the property tree works as key/value pairs, whereas, Yaml-cpp has node designations. In the code i am just trying to assume type of tree node based on value (no value - Map node, else - key/value node)
Apparently, my emitted yaml file doesn't possess the desired tree structure as presented above since my logic is wrong. I would like to make a recursive function which can iterate through any kind of tree and emit it to yaml file.
Is it possible to iterate tree and subsequently emit to yaml recursively? If yes, i would appreciate some ideas.
So I took your desired YAML and put it through an online converter to get a "reliable" ptree representation (which you interestingly left out of the question).
Then I proceeded to do a simple ptree roundtrip for sanity checks:
Live On Coliru
#include <boost/property_tree/json_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
std::istringstream sample_json();
ptree sample_ptree();
int main() {
write_json(std::cout, sample_ptree());
}
std::istringstream sample_json() {
return std::istringstream(R"({
"fibonacci": {
"type": "series",
"entities": {
"golden_ratio": {
"ratio": 2.3
},
"function": {
"power_series": 2
}
}
}
})");
}
ptree sample_ptree() {
ptree pt;
{
auto stream = sample_json();
read_json(stream, pt);
}
return pt;
}
Prints
{
"fibonacci": {
"type": "series",
"entities": {
"golden_ratio": {
"ratio": "2.3"
},
"function": {
"power_series": "2"
}
}
}
}
to_yaml take #1
The simplest is, of course, to read the same JSON, and let yaml-cpp do the conversion:
auto stream = sample_json();
std::cout << YAML::Load(stream) << "\n";
Prints:
{fibonacci: {type: series, entities: {golden_ratio: {ratio: 2.3}, function: {power_series: 2}}}}
to_yaml take #2: pretty print
First off
naming is important. iterator doesn't describe the function, and clashes with the well-known concept from the standard library
the key argument is unused
you only ever BeginMap, how are you expecting a valid tree if you don't have EndMap anywhere in the code?
No global variables please. They make your code brittle (non-deterministic, non-idempotent, non-reentrant, not threadsafe etc.). Just pass that Emitter& as a parameter.
I'd make it MUCH simpler:
void to_yaml(ptree const& node, YAML::Emitter &m_out) {
if (node.empty()) {
m_out << YAML::Value << node.data();
} else {
m_out << YAML::BeginMap;
for (auto const&item : node) {
m_out << YAML::Key << item.first;
to_yaml(item.second, m_out);
}
m_out << YAML::EndMap;
}
}
Now, to have a convenient entry point, add an overload:
std::string to_yaml(ptree const& tree) {
YAML::Emitter out;
to_yaml(tree, out);
return out.c_str();
}
Now you can print the result by doing:
std::cout << to_yaml(sample_ptree()) << "\n";
Prints:
fibonacci:
type: series
entities:
golden_ratio:
ratio: 2.3
function:
power_series: 2
Full Listing
#include <iostream>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
std::istringstream sample_json();
ptree sample_ptree();
#include "yaml-cpp/yaml.h"
void to_yaml(ptree const& node, YAML::Emitter &m_out) {
if (node.empty()) {
m_out << YAML::Value << node.data();
} else {
m_out << YAML::BeginMap;
for (auto const&item : node) {
m_out << YAML::Key << item.first;
to_yaml(item.second, m_out);
}
m_out << YAML::EndMap;
}
}
std::string to_yaml(ptree const& tree) {
YAML::Emitter out;
to_yaml(tree, out);
return out.c_str();
}
int main() {
write_json(std::cout, sample_ptree());
{
auto stream = sample_json();
std::cout << YAML::Load(stream) << "\n";
}
std::cout << to_yaml(sample_ptree()) << "\n";
}
std::istringstream sample_json() {
return std::istringstream(R"({
"fibonacci": {
"type": "series",
"entities": {
"golden_ratio": {
"ratio": 2.3
},
"function": {
"power_series": 2
}
}
}
})");
}
ptree sample_ptree() {
ptree pt;
{
auto stream = sample_json();
read_json(stream, pt);
}
return pt;
}
I have XML file:
<expressions>
<addition id="1">
<item>2</item>
<item>3</item>
<item>4</item>
</addition>
<subtraction id="2">
<minuend>3</minuend>
<subtrahend>2</subtrahend>
</subtraction>
<multiplication id="3">
<factor>5</factor>
<factor>6</factor>
<factor>8</factor>
</multiplication>
<division id="4">
<dividend>54</dividend>
<divisor>9</divisor>
</division>
</expressions>
Need to parse it and provide the result like another XML:
<expressions>
<result id="1">9</result>
<result id="2">1</result>
<result id="3">240</result>
<result id="4">6</result>
</expressions>
Currently I'm investigating the BOOST in area of ptree and read_xml
Please advise where additional information could be found?
Thanks in advance
My current investigation results are:
I have the class which defines Expression with virtual function which evaluates expression, lower on inheritance tree this function should be overridden and implemented in context of expression type.
class AbstractExpression
{
public:
AbstractExpression(ExpressionType aType){}
virtual ~AbstractExpression() {}
// Evaluates expression (must be overrided by child)
virtual int evalExpr() const = 0;
};
Inherited classes are additionExpression, substractionExpression, multiplicationExpression, divisionExpression.
Besides this I have implemented methods of data manipulation in every inherited class.
At the end I've wrote some code, which counts this XML:
using boost::property_tree::ptree;
ptree pt;
read_xml("/PATH_TO/valuator.xml", pt);
const ptree & expressions = pt.get_child("expressions");
BOOST_FOREACH(const ptree::value_type & currExpr, expressions){
std::string readExprType = currExpr.first;
std::cout << "currExpr = " << readExprType << std::endl;
if (readExprType == "addition") {
AbstractExpression *addExpr = new additionExpression();
BOOST_FOREACH(const ptree::value_type & additionNodeEl, currExpr.second){
std::string val = additionNodeEl.second.data();
((additionExpression*)addExpr)->addVal( atoi(val.c_str()) );
}
std::cout << "addition result = " << addExpr->evalExpr() << std::endl;
delete addExpr;
} else if (readExprType == "multiplication") {
AbstractExpression *multExpr = new multiplicationExpression();
BOOST_FOREACH(const ptree::value_type &multNodeEl, currExpr.second) {
std::string val = multNodeEl.second.data();
if (!val.empty())
((multiplicationExpression*)multExpr)->addVal( atoi(val.c_str()) );
}
std::cout << "multiplication node result = " << multExpr->evalExpr() << std::endl;
delete multExpr;
} else if (readExprType == "subtraction") {
AbstractExpression *substrExpr = new substractionExpression();
BOOST_FOREACH(const ptree::value_type &substNodeEl, currExpr.second) {
std::string elTypeName = substNodeEl.first;
std::string val = substNodeEl.second.data();
if (elTypeName == "minuend") {
((substractionExpression*)substrExpr)->setMinuend( atoi(val.c_str()) );
} else if (elTypeName == "subtrahend") {
((substractionExpression*)substrExpr)->setSubtrahend( atoi(val.c_str()) );
}
}
std::cout << "subtraction node result = " << substrExpr->evalExpr() << std::endl;
delete substrExpr;
} else if (readExprType == "division") {
AbstractExpression *divExpr = new divisionExpression();
BOOST_FOREACH(const ptree::value_type &divNodeEl, currExpr.second) {
std::string elTypeName = divNodeEl.first;
std::string val = divNodeEl.second.data();
if ( elTypeName == "dividend" ) {
((divisionExpression*)divExpr)->setDividend( atoi(val.c_str()) );
} else if ( elTypeName == "divisor" ) {
((divisionExpression*)divExpr)->setDivisor( atoi(val.c_str()) );
}
}
std::cout << "dividend node result = " << divExpr->evalExpr() << std::endl;
delete divExpr;
}
}
Now I need to write all these results to XML.
Really, use a proper XML library (TinyXML, RapidXML, PugiXML, libxml2 etc.).
If you really care only about a very specific subset of XML, here's a quick & dirty parser based on Boost Spirit V2: Live On Coliru
namespace /*parser*/
{
namespace qi = boost::spirit::qi;
template <typename It, typename Skipper = qi::space_type>
struct grammar : qi::grammar<It, ast::expressions(), Skipper>
{
grammar() : grammar::base_type(expressions_)
{
using namespace qi;
static const lexeme_type L;
simplevalue_ = auto_; // parses into Value, whatever it was declared as
id_attr = eps >> L["id"] > '=' > '"' > int_ > '"';
complex_attr = eps >> L["complex"] > '=' > '"' > *~char_('"') > '"';
expr_open = eps >> '<' >> L[lit(_r1)] > -id_attr > -complex_attr > '>';
expr_close = eps >> '<' >> '/' > L[lit(_r1)] > '>';
// expression element parsing
addition_ = expr_open(+"addition") > +subexpr_(+"item") > expr_close(+"addition");
subtraction_ = expr_open(+"subtraction") > (subexpr_(+"minuend") > subexpr_(+"subtrahend")) > expr_close(+"subtraction");
multiplication_ = expr_open(+"multiplication") > +subexpr_(+"factor") > expr_close(+"multiplication");
division_ = expr_open(+"division") > (subexpr_(+"dividend") > subexpr_(+"divisor")) > expr_close(+"division");
expression_ = simplevalue_ | addition_ | subtraction_ | multiplication_ | division_;
subexpr_ = eps >> '<' >> L[lit(_r1)] > '>' > expression_ > '<' > '/' > L[lit(_r1)] > '>';
expressions_ = eps
> '<' > L["expressions"] > '>'
> *expression_
> expr_close(+"expressions");
BOOST_SPIRIT_DEBUG_NODES((simplevalue_)(expr_open)(expr_close)(subexpr_)(addition_)(subtraction_)(multiplication_)(division_)(expression_)(expressions_))
}
private:
template <typename... T>
using Rule = qi::rule<It, T..., qi::space_type>;
// tags/primitives
Rule<> complex_attr;
Rule<int()> id_attr;
Rule<ast::Value()> simplevalue_;
Rule<ast::Id(std::string element_name)> expr_open;
Rule<void(std::string element_name)> expr_close;
Rule<ast::expression(std::string element_name )> subexpr_;
// compounds
Rule<ast::addition()> addition_;
Rule<ast::subtraction()> subtraction_;
Rule<ast::multiplication()> multiplication_;
Rule<ast::division()> division_;
Rule<ast::expression()> expression_;
Rule<ast::expressions()>
expressions_;
};
} /*parser*/
It parses the input into an abstract syntax tree that can be visited to evaluate the (sub) expressions. The test driver
int main()
{
std::ifstream ifs("expressions.xml");
It f(ifs >> std::noskipws), l;
try {
ast::expressions parsed;
grammar<It> g;
// parsing
bool ok = phrase_parse(f, l, g, qi::space, parsed);
// output results
if (ok)
{
eval::machine machine;
std::cout << "<expressions>\n";
for (auto& expr : parsed)
if (get_id(expr))
std::cout << "\t<result id=\"" << *get_id(expr) << "\">" << machine.evaluate(expr) << "</result>\n";
std::cout << "</expressions>\n";
}
} catch(qi::expectation_failure<It> const& e) {
std::cout << "Expected " << e.what_ << " at '" << std::string(e.first,e.last) << "'\n";
}
}
Prints
<expressions>
<result id="1">9</result>
<result id="2">1</result>
<result id="3">240</result>
<result id="4">6</result>
</expressions>
Live On Coliru
Note: doesn't handle comments, unicode, processing instructions, namespaces, PCDATA, character entity references etc. In short This is not an XML parser
I have the following XML file and I want to store it using the below structures.
the data structs:
struct transitions
{
string oldstate;
string event;
string newstate;
};
struct XML_Diagram
{
string diag_name;
string diag_defaultstate;
list<string> diag_states;
list<string> diag_events;
list<transitions> diag_transitions;
};
the xml file:
<diagram>
<diagname>DiagaX</diagname>
<states>
<state>A</state>
.............
</states>
<events>
<event>ev1</event>
.................
</events>
<defaultstate>A</defaultstate>
<transitions>
<transition>
<oldstate>A</oldstate>
<event>ev1</event>
<newstate>B</newstate>
</transition>
<transition>
<oldstate>B</oldstate>
<event>ev2</event>
<newstate>C</newstate>
</transition>
.........................
</transitions>
</diagram>
It is clear to me how can I to access diagram.states .
I can do it with the folowing code:
using boost::property_tree::ptree;
ptree pt;
// Get diagram states
BOOST_FOREACH(ptree::value_type &v, pt.get_child("diagram.states"))
{
diag_states.push_back(v.second.data());
}
What is not clear to me is how can I access the data from at the level diagram.transitions.transition ?
My problem is that I could not find any examples in the documentation on how to parse more complex xml files with several levels.
This useful utility function traverses and pretty-prints an entire property tree:
using boost::property_tree::ptree;
std::string q(const std::string& s) { return "\"" + s + "\""; }
void print_tree(const ptree& pt, int level)
{
const std::string sep(2 * level, ' ');
BOOST_FOREACH(const ptree::value_type &v, pt) {
std::cout
<< sep
<< q(v.first) << " : " << q(v.second.data()) << "\n";
print_tree(v.second, level + 1);
}
}
void print_tree(const ptree& pt) { print_tree(pt, 0); }
The v.second values are trees themselves that can be accessed with the usual get methods. The transitions
can for example be accessed and printed like this:
using std::string;
void print_transitions(const ptree& pt)
{
BOOST_FOREACH(
const ptree::value_type &v,
pt.get_child("diagram.transitions"))
{
const ptree& child = v.second;
std::cout
<< "Event "
<< child.get<string>("event")
<< " in state "
<< child.get<string>("oldstate")
<< " leads to state "
<< child.get<string>("newstate")
<< "\n";
}
}
Here is another example of how to print ptree with attributes:
namespace pt = boost::property_tree;
// worker
typedef std::pair<const pt::ptree&, unsigned> tree_printer;
// empty ptree helper
const pt::ptree& empty_ptree()
{
static pt::ptree t;
return t;
}
std::ostream& operator <<(std::ostream& os, const tree_printer& p)
{
const pt::ptree& tree = p.first;
if(tree.empty()) return os;
const std::string indent(p.second, ' ');
BOOST_FOREACH(const pt::ptree::value_type& v, tree)
{
const std::string& nodeName = v.first;
if(nodeName == "<xmlattr>") continue;
os << indent << nodeName;
const pt::ptree& attributes =
v.second.get_child("<xmlattr>", empty_ptree());
if(!attributes.empty())
{
os << " [ ";
BOOST_FOREACH(const pt::ptree::value_type& attr, attributes)
{
const std::string& attrName = attr.first;
const std::string& attrVal = attr.second.data();
os << attrName << " = '" << attrVal << "'; ";
}
os << "]";
}
os << "\n";
const pt::ptree& childNode = v.second;
os << tree_printer(childNode, p.second + 1);
}
return os;
}
std::ostream& operator <<(std::ostream& os, const pt::ptree& tree)
{
os << tree_printer(tree, 0);
return os;
}
Hope this helps.