Getting the ptree from boost::property_tree::ptree::iterator - c++

I have a piece of code that iterates over a boost property tree (XML).
I need a ptree of the current node, not the children of the node.
UPDATE
xml tree
<node id="A.html">
<subnode> child A1 </subnode>
<subnode> child A2 </subnode>
</node>
<node id="B.html">
<subnode> child B1 </subnode>
<subnode> child B2 </subnode>
</node>
itteration code
void parse_tree(ptree& pt, std::string key)
{
string nkey;
if (!key.empty())
nkey = key + ".";
ptree::const_iterator end = pt.end();
for(ptree::iterator it = pt.begin(); it != end; ++it){
//if the node's id is a .html filname, save the node to file
string id = it->second.get("<xmlattr>.id","");
if(id.find("B.html") != std::string::npos){ //Let's just test for "B.html"
write_xml("test.html", pt); //saves entire tree
write_xml("test.html", it->second); //saves only children of the node
}
parse_tree(it->second, nkey + it->first); //recursion
}
}
Results using write_xml("test.html", pt)
(We get the entire tree, we only want the node)
<node id="A.html">
<subnode> child A1 </subnode>
<subnode> child A2 </subnode>
</node>
<node id="B.html">
<subnode> child B1 </subnode>
<subnode> child B2 </subnode>
</node>
Results using write_xml("test.html", it->second)
(We have no parent node.. only child nodes)
<subnode> child B1 </subnode>
<subnode> child B2 </subnode>
Desired result
(We want the node, and it's children,.. like so)
<node id="B.html">
<subnode> child B1 </subnode>
<subnode> child B2 </subnode>
</node>

UPDATE 2
Rewritten in response to the comment/updated question.
There are two ways.
You can use the undocumented function write_xml_element to write the single element (using the key as element name):
// write the single element: (undocumented API)
boost::property_tree::xml_parser::write_xml_element(
std::cout, it->first, it->second,
0, settings
);
or you can create a new ptree object with the single child
ptree tmp;
tmp.add_child(it->first, it->second);
write_xml(std::cout, tmp, settings);
Live On Coliru
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <fstream>
#include <iostream>
using namespace boost::property_tree;
void parse_tree(ptree& pt, std::string key)
{
std::string nkey;
auto settings = xml_parser::xml_writer_make_settings<std::string>('\t', 1);
if (!key.empty()) {
nkey = key + ".";
}
ptree::const_iterator end = pt.end();
for(ptree::iterator it = pt.begin(); it != end; ++it)
{
//if the node's id an .html filname, save the node to file
std::string id = it->second.get("<xmlattr>.id","");
if (id.find(key) != std::string::npos) {
// write the single element: (undocumented API)
boost::property_tree::xml_parser::write_xml_element(
std::cout, it->first, it->second,
0, settings
);
// or: create a new pt with the single child
std::cout << "\n==========================\n\n";
ptree tmp;
tmp.add_child(it->first, it->second);
write_xml(std::cout, tmp, settings);
}
parse_tree(it->second, nkey + it->first); //recursion
}
}
int main() {
ptree pt;
read_xml("input.txt", pt);
parse_tree(pt, "B");
}
Output:
<node id="B.html">
<subnode> child B1 </subnode>
<subnode> child B2 </subnode>
</node>
==========================
<?xml version="1.0" encoding="utf-8"?>
<node id="B.html">
<subnode> child B1 </subnode>
<subnode> child B2 </subnode>
</node>

Related

Using pugixml to read an entire xml file

I know there's already a way to loop through a file with pugi::xml_node::traverse, but I'm very interested in how things work, so I want to reimplement it using a recursive function.
Currently, I can only parse the first depth of the function because I don't know how to detect whether the current item has children (next_siblings returns an invalid value).
// TODO: use std::ostringstream instead of std::string
void MyClass::recursive(const pugi::xml_node& start, std::string& output)
{
// Check for invalid node
if (!start.first_child() || (!start.next_sibling() && start.parent() != start.parent())) {
return;
}
// Process the current node
for (auto node : start.children()) {
output += node.name();
output += "\n";
for (auto attribute : node.attributes()) {
output += "Attribute Name : ";
output += attribute.name();
output += ", Attribute Value = ";
output += attribute.value();
output += " ";
}
output += "\n";
const char* PCDATA = node.child_value();
output += PCDATA == "" ? "[no pcdata]"
: PCDATA;
if (node.first_child()) {
recursive(node, output);
}
else {
recursive(node.next_sibling(), output);
}
}
}
Sample XML file
<?xml version="1.0" encoding="UTF-8"?>
<root>
<child1>
<sub name="attr1">value</sub>
<sub name="attr2">value</sub>
<sub name="attr3">value</sub>
</child1>
<child2>
<sub name="attr1">value</sub>
<sub name="attr2">value</sub>
<sub name="attr3">value</sub>
</child2>
<child3>
<sub name="attr1">value</sub>
<sub_with_children>
<child1 name="[]">value</sub>
<child2 name="[]">value</sub>
<child3 name="[]">value</sub>
</sub_with_children>
</child3>
<child4>
<sub name="attr1">value</sub>
<sub name="attr2">value</sub>
</child4>
</root>
Edit: the code above is now working

How to erase child from boost tree if it is double registered?

Could you please help me with the following?
I populate this finalTree = treeA + treeB
However, the problem is that some elements of treeB have the same name with some of treeA. As a result I might have double registries for some children.
ie.
<category>
<fruit type="banana">
<characteristic>
<point v="0"/>
</characteristic>
</fruit>
<fruit type="orange">
<characteristic>
<point v="1"/>
</characteristic>
</fruit>
<fruit type="banana">
<characteristic>
<point v="2"/>
</characteristic>
</fruit>
<fruit type="fig">
<characteristic>
<point v="3"/>
</characteristic>
</fruit>
</category>
What I want to achieve is to delete the first entry of banana and keep the last.
So far I do:
boost::property_tree::ptree & node = informationTree.add("information.fruitTypes", "");
node.add("<xmlattr>.type", fruit);
node.add_child("characteristic", char);
The problem is that I don't know how to remove it, as I don't know whether the double entry will be banana or something else the next time. Should I copy populate tree? What do you suggest please?
If you're just building the tree, you can just use put_* instead of add_* and it would overwrite an element if it already exists by that name.
If you have a tree and want to remove the duplicates at a certain subtree, you have to do it manually, e.g.:
Live On Coliru
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>
#include <map>
using boost::property_tree::ptree;
template <typename KeyF>
ptree nodup(ptree const& pt, KeyF key_accessor) {
ptree filtered;
std::map<std::string, std::reference_wrapper<ptree> > seen;
for (auto& entry : pt) {
auto key = key_accessor(entry);
auto previous = seen.find(key);
if (seen.end() == previous)
seen.emplace(key, filtered.add_child(entry.first, entry.second));
else
previous->second.get() = entry.second; // overwrite
}
return filtered;
}
int main() {
ptree pt;
{
std::istringstream iss( "<category><fruit type=\"banana\"><characteristic><point v=\"0\"/></characteristic></fruit><fruit type=\"orange\"><characteristic><point v=\"1\"/></characteristic></fruit><fruit type=\"banana\"><characteristic><point v=\"2\"/></characteristic></fruit><fruit type=\"fig\"><characteristic><point v=\"3\"/></characteristic></fruit></category>");
read_xml(iss, pt);
}
write_xml(std::cout, pt, boost::property_tree::xml_writer_make_settings<std::string>(' ', 4, "utf-8"));
auto& subtree = pt.get_child("category");
subtree = nodup(subtree, [](ptree::value_type const& item) { return item.second.get("<xmlattr>.type", ""); });
write_xml(std::cout, pt, boost::property_tree::xml_writer_make_settings<std::string>(' ', 4, "utf-8"));
}
If you are a bit more performance concerned, you can iterate backwards and avoid some overwrite actions:
Live On Coliru
template <typename KeyF>
ptree nodup(ptree const& pt, KeyF key_accessor) {
ptree filtered;
std::map<std::string, std::reference_wrapper<ptree> > seen;
for (auto entry = pt.rbegin(), last = pt.rend(); entry != last; ++entry) {
auto key = key_accessor(*entry);
auto previous = seen.find(key);
if (seen.end() == previous)
seen.emplace(key, filtered.add_child(entry->first, entry->second));
}
return filtered;
}
However, keep in mind this potentially alters the order in which the fruits appear in the subtree.

Lastchild in xml file using QDomDocument Class

i have this xml:
<VCAAnalysis>
<VCAStream>
<VCAFrame width="768" height="432" rtptime="" utctime="102157000" utctimeHigh="0" configID="0" />
<VCAFrame width="768" height="432" rtptime="" utctime="102157160" utctimeHigh="0" configID="0">
<Object objectID="138.96.200.59_20160126_102157160_1" minX="276" minY="0" maxX="320" maxY="123" width="44" height="123" ObjPropTag="PERSON">
</Object>
</VCAFrame>
<VCAFrame width="768" height="432" rtptime="" utctime="102157320" utctimeHigh="0" configID="0" />
<VCAFrame width="768" height="432" rtptime="" utctime="102157480" utctimeHigh="0" configID="0">
<Object objectID="138.96.200.59_20160126_102157480_2" minX="224" minY="264" maxX="287" maxY="343" width="63" height="79" ObjPropTag="PERSON">
</Object>
</VCAFrame>
<VCAFrame width="768" height="432" rtptime="" utctime="102157640" utctimeHigh="0" configID="0">
<Object objectID="138.96.200.59_20160126_102157480_3" minX="204" minY="266" maxX="331" maxY="400" width="127" height="134" ObjPropTag="PERSON">
</Object>
</VCAFrame>
<VCAFrame width="768" height="432" rtptime="" utctime="102157000" utctimeHigh="0" configID="0" />
</VCAStream>
</VCAAnalysis>
I want to get the last objectID(138.96.200.59_20160126_102157480_3) in the last VCAFrame which have an object.
i tried this code but it doesn't work.
QDomNodeList a = VCAStream.elementsByTagName("VCAFrame");
if(a.size()!=0) {
QDomElement lastobj = VCAStream.lastChild().toElement();
QDomElement last = lastobj.firstChild().toElement();
QString lastid = last.attribute("objectID");
cout << qPrintable("laaaaaaaast "+lastid) << endl;
}
This worked for me:
QDomNodeList vcaStreams = VCAStream.elementsByTagName("VCAStream");
QDomNodeList vcaFrames = vcaStreams.at(0).childNodes(); //Gives 6 VCAFrame tags
QDomNodeList vcaObjects = vcaFrames.at(4).childNodes(); //Gives 1 Object tag
qDebug() << vcaObjects.at(0).toElement().attribute("objectID");
lastobj in your code refers to the last VCAFrame, which does not have an objectID.
EDIT: If you need to iterate over an entire xml file. I'm assuming that you want the last vcaFrame that has an objectID in each VCAStream.
QDomNodeList vcaStreams = VCAStream.elementsByTagName("VCAStream");
for (int i = 0; i < vcaStreams.count(); ++i) {
QDomNodeList vcaFrames = vcaStreams.at(i).childNodes(); //Gives us all VCAFrameTags
//Find last tag with objectID
QDomElement last;
for (int j = vcaFrames.count() - 1; j >= 0; --j) {
//Assumes there is at most one <object> tag in each VCAFrame
if (vcaFrames.at(j).hasChildNodes()) {
QDomElement tmp = vcaFrames.at(j).firstChild().toElement();
if (tmp.hasAttribute("objectID")) {
last = tmp;
break;
}
}
}
//last now holds the last VCAFrame with an object tag or is Null
if (last.isNull())
qDebug() << "No objectID found";
else
qDebug() << last.attribute("objectID");
}
I tested this on your XML file and it gave me the correct result, but I did not try adding more than one VCAStream tag.

Boost Subgraph Implementation and exporting using Graphml

I am using boost 1.53.0, I have implemented a small demo using boost for subgraph.
I need to export the subgraph information in the graphml file, while exporting it will create all the nodes in the parent graph but not able to hold the information about it's subraphs.
so please help me if there is any way to hold information about subgraph?.
My implementation is exporting as follows:
enter code here
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<graph id="G" edgedefault="undirected" parse.nodeids="free"
parse.edgeids="canonical" parse.order="nodesfirst">
<node id="n0">
</node>
<node id="n1">
</node>
<node id="n2">
</node>
<node id="n3">
</node>
<node id="n4">
</node>
<node id="n5">
</node>
<edge id="e0" source="n0" target="n1">
</edge>
<edge id="e1" source="n1" target="n2">
</edge>
<edge id="e2" source="n1" target="n3">
</edge>
<edge id="e3" source="n4" target="n1">
</edge>
<edge id="e4" source="n4" target="n5">
</edge>
<edge id="e5" source="n5" target="n3">
</edge>
<edge id="e6" source="n2" target="n5">
</edge>
</graph>
</graphml>
Actully the nodes n0,n1,n2 are members of subgraph G1 and n4,n5 are members of subgrph G2.
G0 is main parent graph.
Following is the way to deal with above problem. This problem is solved by combining the use of dynamic properties from boost and also using bundled properties from boost.
this code works for boost 1_53_0.
Reference property map is used for storing the properties of boost subgraph.
#include <QtCore/QCoreApplication>
#include <boost/config.hpp>
#include <iostream>
#include <algorithm>
#include <boost/graph/adjacency_list.hpp>
#include <boost/property_map/property_map.hpp>
#include <string>
#include <boost/graph/subgraph.hpp>
#include <QMap>
using namespace std;
using namespace boost;
enum graph_IDproperty_t
{
graph_IDproperty
};
namespace boost
{
BOOST_INSTALL_PROPERTY(graph,IDproperty);
}
struct GraphProperties {
std::string strName;
std::string id;
};
typedef boost::subgraph<boost::adjacency_list< boost::listS,
boost::vecS,
boost::bidirectionalS,
boost::property<boost::vertex_index_t, int , property<boost::vertex_color_t, boost::default_color_type > > ,
boost::property<boost::edge_index_t,int, property<boost::edge_color_t , default_color_type> > ,
boost::property<graph_IDproperty_t,GraphProperties > > >
Graph;
Graph gMainGraph;
typedef QMap<Graph*,GraphProperties*> mapGraphToProperty;
mapGraphToProperty getMap(Graph& graph);
void graphMapRecur(mapGraphToProperty& map, Graph& graph);
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Graph& subG = gMainGraph.create_subgraph();
Graph& subG1 = gMainGraph.create_subgraph();
boost::ref_property_map<Graph*, GraphProperties>
graph_propt1(boost::get_property(subG1,graph_IDproperty));
graph_propt1[&subG1].id = "SubG1";
cout<<graph_propt1[&subG1].id<<endl;
boost::ref_property_map<Graph*, GraphProperties>
graph_propt(boost::get_property(subG,graph_IDproperty));
graph_propt[&subG].id = "SubG";
cout<<graph_propt[&subG].id<<endl;
boost::ref_property_map<Graph*, GraphProperties>
graph_proptMain(boost::get_property(gMainGraph,graph_IDproperty));
graph_proptMain[&gMainGraph].id = "gMain";
cout<<graph_proptMain[&gMainGraph].id<<endl;
mapGraphToProperty map = getMap(gMainGraph);
boost::ref_property_map<Graph*, GraphProperties>
graph_proptMain1(*(map.value(&gMainGraph)));
boost::ref_property_map<Graph*, GraphProperties>
graph_proptsubG(*(map.value(&subG)));
boost::ref_property_map<Graph*, GraphProperties>
graph_proptsubG1(*(map.value(&subG1)));
cout<<"Main G Value : "<<graph_proptMain1[&gMainGraph].id<<endl;
cout<<"Sub G Value : "<<graph_proptsubG[&subG].id<<endl;
cout<<"Sub G1 Value : "<<graph_proptsubG1[&subG1].id<<endl;
cout<<"Map Value Main: "<<(map.value(&gMainGraph))<<endl;
cout<<"Map Value SubG: "<<(map.value(&subG))<<endl;
cout<<"Map Value SubG1b: "<<(map.value(&subG1))<<endl;
return a.exec();
}
mapGraphToProperty getMap(Graph &graph)
{
mapGraphToProperty map;
graphMapRecur(map,graph);
return map;
}
void graphMapRecur(mapGraphToProperty &map, Graph &graph)
{
Graph::children_iterator itrSubgraph, itrSubgraph_end;
for (boost::tie(itrSubgraph, itrSubgraph_end) = (graph).children(); itrSubgraph != itrSubgraph_end; ++itrSubgraph)
{
graphMapRecur(map,(*itrSubgraph));
}
GraphProperties* gp = &(get_property(graph,graph_IDproperty));
map.insert(&graph,gp);
cout<<"Recurrr"<<endl;
}

boost::property_:tree - parsing and processing data

I have just discovered boost::property_tree, which seems the perfect answer to my problem. I wrote a small test program to extract specific data from an xml file. I have used the example provided in the documentation as a guide.
The xml file: test.xml:
<section>
<GROUP>
<g_name>ABC</g_name>
<fields>
<row>
<name>A</name>
<datatype>string</datatype>
<field_size>6</field_size>
<value>ABC</value>
</row>
<row>
<name>B</name>
<datatype>integer</datatype>
<field_size>5</field_size>
<value>00107</value>
</row>
<row>
<name>C</name>
<datatype>string</datatype>
<field_size>20</field_size>
<value>LOTS OF LETTERS </value>
</row>
</fields>
</GROUP>
<GROUP>
<g_name>CDE</g_name>
<fields>
<row>
<name>A</name>
<datatype>string</datatype>
<field_size>6</field_size>
<value>CDE</value>
</row>
<row>
<name>B</name>
<datatype>integer</datatype>
<field_size>5</field_size>
<value>00100</value>
</row>
<row>
<name>F</name>
<datatype>integer</datatype>
<field_size>4</field_size>
<value>1970</value>
</row>
</fields>
</GROUP>
</section>
The code:
using boost::property_tree::ptree;
struct t_collection
{
ptree pt;
void load(const std::string &filename);
void print();
};
void t_collection::load(const std::string &filename)
{
read_xml(filename, pt);
}
void t_collection::print()
{
BOOST_FOREACH(ptree::value_type &v, pt.get_child("section.GROUP"))
BOOST_FOREACH(ptree::value_type &v, pt.get_child("section.GROUP"))
{
printf("X: %s->", v.second.data().c_str());
//prints X: ABC ->
BOOST_FOREACH(ptree::value_type &w, pt.get_child("section.GROUP.fields.row"))
printf("%s\n", w.second.data().c_str());
//prints A, string, 6, ABC - that is good for first iteration but there should be 3 iterations here
}
//then prints X: and just "" and repeats the set from the first one
}
int main()
{
try
{
t_collection t1;
t1.load("test.xml");
t1.print();
}
catch (std::exception &e)
{
std::cout << "Error: " << e.what() << "\n";
}
return 0;
}
Note: I am trying to extract the values (ABC and the inner values, like A - string - 6 - ABC, for each GROUP - and each set of "row", which I will process and then output in a different format). Please see comment in code for something I tried.
So far the best result was with: (contents inside print():
BOOST_FOREACH(ptree::value_type &z, pt.get_child("section"))
//BOOST_FOREACH(ptree::value_type &v, pt.get_child("section.GROUP"))
{
printf("X: %s->", pt.get<std::string>("section.GROUP.g_mame", "default").c_str());
//prints X: ABC ->
BOOST_FOREACH(ptree::value_type &w, pt.get_child("section.GROUP.fields.row"))
{
printf("%s\n", pt.get<std::string>("section.GROUP.fields.row.name", "name").c_str());
printf("%s\n", pt.get<std::string>("section.GROUP.fields.row.datatype", "type").c_str());
printf("%s\n", pt.get<std::string>("section.GROUP.fields.row.field_size", "size").c_str());
printf("%s\n", pt.get<std::string>("section.GROUP.fields.row.value", "value").c_str());
}
}
//prints x: default->A, string, 6, ABC (3 times) then repeat identically
I can't get the data from more than one record ! Please help, give me a suggestion - what am I doing wrong ?
Thank you.
You are missing a level in your iteration. You need to iterate over the elements that have multiple children with the same name.
std::pair<ptree::const_assoc_iterator, ptree::const_assoc_iterator>
r(pt.get_child("section").equal_range("GROUP"));
for (ptree::const_assoc_iterator i(r.first); i != r.second; ++i) {
// Do something with each group.
}
Repeat as appropriate as you descend the tree.