I am using boost libraries to parse XML files and I have to create a ptree manually.
I want to create below xml file using boost ptree.
<?xml version="1.0"?>
<Txn>
<Resp errCode="0" errInfo="" />
<A exptime="20171230">xyz Information</A>
<B>xyz Information</B>
<C type="Active">xyz Information</C>
</Txn>
To achieve above xml,
Below is my sample code:
boost::property_tree::ptree pt;
boost::property_tree::ptree ptr1;
boost::property_tree::ptree ptr2;
boost::property_tree::ptree ptr3;
ptr1.put("<xmlattr>.errCode", Txn.resp.errCode);
ptr1.put("<xmlattr>.errInfo", Txn.resp.errInfo);
ptr2.push_back(boost::property_tree::ptree::value_type("A", boost::property_tree::ptree(data)));
ptr2.push_back(boost::property_tree::ptree::value_type("C", boost::property_tree::ptree(data)));
ptr2.put("A.<xmlattr>.exptime", data);
ptr2.put("C.<xmlattr>.type", data);
ptr3.put("<xmlattr>", data);
pt.add_child("Txn.Resp", ptr1);
pt.add_child("Txn", ptr2);
pt.add_child("Txn.B", ptr3);
Here Child A and C always create as a separate with Parent Txn But I want to add all child in Txn Parent
. I did not understand, why child A and C are different here.
It would be very helpful, If someone help me to provide right way.
Here's the simplest thing I can think of:
Live On Coliru
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
static auto pretty = boost::property_tree::xml_writer_make_settings<std::string>(' ', 4);
int main() {
ptree root;
root.add("Txn.Resp.<xmlattr>.errCode", 0);
root.add("Txn.Resp.<xmlattr>.errInfo", "");
root.add("Txn.A", "xyz Information");
root.add("Txn.A.<xmlattr>.exptime", "20171230");
root.add("Txn.B", "xyz Information");
root.add("Txn.C", "xyz Information");
root.add("Txn.C.<xmlattr>.type", "Active");
write_xml(std::cout, root, pretty);
}
Prints:
<?xml version="1.0"?>
<Txn>
<Resp errCode="0" errInfo="" />
<A exptime="20171230">xyz Information</A>
<B>xyz Information</B>
<C type="Active">xyz Information</C>
</Txn>
Key point is to create the element node before adding attributes, otherwise you get this instead:
Live On Coliru
<?xml version="1.0" encoding="utf-8"?>
<Txn>
<Resp errCode="0" errInfo=""/>
<A exptime="20171230"/>
<A>xyz Information</A>
<B>xyz Information</B>
<C type="Active"/>
<C>xyz Information</C>
</Txn>
Related
I have a problem parsing document with pugi lib in c++.
I'm completely new to parsing in c++.
Here is my svg file:
<?xml version='1.0' encoding='UTF-8'?>
<!-- This file was generated by dvisvgm 2.9.1 -->
<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='260.973912pt' height='53.742909pt' viewBox='229.120747 -42.285873 260.973912 53.742909'>
<defs>
<font id='cmr10' horiz-adv-x='0'>
<font-face font-family='cmr10' units-per-em='1000' ascent='750' descent='250'/>
</font>
<font id='cmmi10' horiz-adv-x='0'>
<font-face font-family='cmmi10' units-per-em='1000' ascent='750' descent='250'/>
</font>
</defs>
<style type='text/css'>
<![CDATA[text.f0 {font-family:cmmi10;font-size:9.96264px}
text.f1 {font-family:cmr10;font-size:9.96264px}
]]>
</style>
<g id='page1'>
<text class='f1' x='485.11332' y='-35.865504'>1</text>
<text class='f0' x='229.120747' y='11.457036'>abc</text>
</g>
</svg>
And here is my code:
#include "pugixml.hpp"
#include "pugixml.cpp"
int main(int argc, char* argv[])
{
std::string svg_file_path = "path_to_file.xml";
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(svg_file_path.c_str(), pugi::parse_default | pugi::parse_declaration);
pugi::xml_node root = doc.document_element();
return 0;
}
However when I debug I see that the variable root is still undefined. In the meantime, the value of pugi::parse_default is recognized as 116U while the value of pugi::parse_declaration is recognized as 256U.
For the result variable it writes status = status_ok(0)
What could I possible do wrong? How do I access such things as content of children? Their class (f0, f1) or finally, data CDATA where I need to know the correspondence {"f0": "cmmi10"} and {"f1": "cmr10"}
?
Yaroslav.
Lets say I am aiming to create an xml in the following form:
<main>
<elements>
<element name="elem1"><temp/>
</element>
<element name="elem2"><temp/>
</element>
</elements>
</main>
I have the following code:
ptree pt;
pt.put("main","");
ptree temp1;
temp1.put("element","");
temp1.put("element.<xmlattr>.name","elem1");
temp1.put("element.temp");
ptree temp2;
temp2.put("element","");
temp2.put("element.<xmlattr>.name","elem2");
temp2.put("element.temp");
//temp1 represents the 1st <element>...</element>
//temp2 represents the 1st <element>...</element>
//Add temp1 and temp2 to <main> under <elements>
I would assume the following would work:
pt.add_child("main.elements",temp1);
pt.add_child("main.elements",temp2);
However this generates the following xml:
<main>
<elements>
<element name="elem1"><temp/>
</element>
</elements>
<elements>
<element name="elem2"><temp/>
</element>
<elements>
</main>
I was able to get the required xml file by making my temp1 in the following form:
temp1.put("<xmlattr>.name","elem1");
temp1.put("temp","");
temp2.put("<xmlattr>.name","elem2");
temp2.put("temp","");
pt.add_child("main.elements.element",temp1);
pt.add_child("main.elements.element",temp2);
Is there a way I can work with my initial temp1 and temp2 nodes to get the desired xml structure?
Your situation is a bit suboptimal (and I'd very much favour the working snippet you gave).
Here's what would work:
pt.add_child("main.elements.element", temp1.get_child("element"));
pt.add_child("main.elements.element", temp2.get_child("element"));
Live On Coliru
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
ptree temp1;
temp1.put("element.<xmlattr>.name","elem1");
temp1.put_child("element.temp", {});
ptree temp2;
temp2.put("element.<xmlattr>.name","elem2");
temp2.put("element.temp", "");
//Add temp1 and temp2 to <main> under <elements>
ptree pt;
pt.add_child("main.elements.element", temp1.get_child("element"));
pt.add_child("main.elements.element", temp2.get_child("element"));
write_xml(std::cout, pt, boost::property_tree::xml_writer_make_settings<std::string>(' ', 4, "utf-8"));
}
Prints
<?xml version="1.0" encoding="utf-8"?>
<main>
<elements>
<element name="elem1">
<temp/>
</element>
<element name="elem2">
<temp/>
</element>
</elements>
</main>
Is there a way in C++, using Tinyxml , TinyXpath , such that, a string containing :
<ns:abcd>
<ns:defg>
<ns:hijk>
</ns:hijk>
</ns:defg>
</ns:abcd>
transforms to
<abcd>
<defg>
<hijk>
</hijk>
</defg>
</abcd>
EDIT:
I was using Tinyxml and Tinyxpath.
My workflow was :
a) Create a dom-tree using TinyXML
b) Pass dom-tree to Tinyxpath for xpath evaluations
To add namespace removal, I used following function :
void RemoveAllNamespaces(TiXmlNode* node)
{
TiXmlElement* element = node->ToElement();
if(!element){
return;
}
std::string elementName = element->Value();
std::string::size_type idx = elementName.rfind(':');
if(idx != std::string::npos)
{
element->SetValue(elementName.substr( idx + 1).c_str());
}
TiXmlNode* child = element->IterateChildren(NULL);
while(child)
{
RemoveAllNamespaces(child);
child = element->IterateChildren(child);
}
}
So workflow changed to :
a) Create a dom-tree using TinyXML
b) Remove namespace from the domtree using RemoveAllNamespaces(domtree.Root() )
c) Pass modified-dom-tree to Tinyxpath for xpath evaluations
I would employ an XSLT transform here:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:template match="*">
<xsl:element name="{name()}" namespace=""><xsl:apply-templates select="node()|#*"/></xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{name()}" namespace=""><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Note that on elements/attribute, namespace="" clears the namespace. You can also specify a different namespace instead.
With input.xml like
<?xml version="1.0"?>
<ns:abcd xmlns:ns="http://bla/bla">
<ns:defg attr="value">
<ns:hijk>
</ns:hijk>
</ns:defg>
</ns:abcd>
xsltproc xform.xsl input.xml prints:
<abcd>
<defg attr="value">
<hijk>
</hijk>
</defg>
</abcd>
Ok, in response to the edited question, a few notes:
that doesn't actually treat the namespaces (consider xmlns="http://blabla.com/uri" style default namespaces), but that's actually a TinyXml limitation (eek):
Further, TinyXML has no facility for handling XML namespaces. Qualified element or attribute names retain their prefixes, as TinyXML makes no effort to match the prefixes with namespaces.
it doesn't treat attributes (which can also be qualified)
Here's what I'd do a quick & dirty (assumes TIXML_USE_STL as you were supposedly already using):
static inline std::string RemoveNs(std::string const& xmlName)
{
return xmlName.substr(xmlName.find_last_of(":") + 1);
}
void RemoveAllNamespaces(TiXmlNode* node)
{
assert(node);
if (auto element = node->ToElement()) {
element->SetValue(RemoveNs(element->Value()));
for (auto attr = element->FirstAttribute(); attr; attr = attr->Next())
attr->SetName(RemoveNs(attr->Name()));
for (auto child = node->IterateChildren(nullptr); child; child = element->IterateChildren(child))
RemoveAllNamespaces(child);
}
}
On my MSVC test it prints
<?xml version="1.0" standalone="no"?>
<!-- Our: to do list data -->
<ToDo a="http://example.org/uri1">
<!-- Do I need: a secure PDA? -->
<Item priority="1" distance="close">Go to the<bold>Toy store!</bold></Item>
<Item priority="2" distance="none">Do bills</Item>
<Item priority="2" distance="far & back">Look for Evil Dinosaurs!</Item>
</ToDo>
What I want is this:
<tree>
<objects>
<object id="12345678">
<AdditionalInfo>
<Owner>Mr. Heik</Owner>
<Health>37/100</Health>
</AdditionalInfo>
</object>
</objects>
</tree>
What I get is this:
<tree>
<objects>
<object id="12345678"/>
<AdditionalInfo>
<Owner>Mr. Heik</Owner>
<Health>37/100</Health>
</AdditionalInfo>
</objects>
</tree>
What I tried is this:
using boost::property_tree::ptree;
ptree pt;
boost::property_tree::ptree nodeObject;
nodeObject.put("object.<xmlattr>.id", 12345678);
boost::property_tree::ptree nodeInfo;
nodeInfo.put("Owner", "Mr. Heik");
nodeInfo.put("Health", "37/100");
// Put everything together
nodeObject.put_child("AdditionalInfo", nodeInfo);
pt.add_child("tree.objects", nodeObject);
write_xml("output.xml", pt);
I tried to get the desired result by using put/add/add_child/etc. but without success. Which boost-functions do I have to use?
This line :
nodeObject.put("object.<xmlattr>.id", 12345678);
Is adding a new child to the subpath "object" of the current node with the given attribute.
Just set your attribute on the Node:
nodeObject.put("<xmlattr>.id", 12345678);
And add the node directly to the correct path in your tree:
pt.add_child("tree.objects.object", nodeObject);
Final code :
ptree pt;
boost::property_tree::ptree nodeObject;
nodeObject.put("<xmlattr>.id", 12345678);
boost::property_tree::ptree nodeInfo;
nodeInfo.put("Owner", "Mr. Heik");
nodeInfo.put("Health", "37/100");
nodeObject.put_child("AdditionalInfo", nodeInfo);
pt.add_child("tree.objects.object", nodeObject);
write_xml("output.xml", pt);
Output :
<?xml version="1.0" encoding="utf-8"?>
<tree>
<objects>
<object id="12345678">
<AdditionalInfo>
<Owner>Mr. Heik</Owner>
<Health>37/100</Health>
</AdditionalInfo>
</object>
</objects>
</tree>
yester day was my first attempt. I am trying to catch the variable "time" in the following "new.xml" file
<?xml version="1.0" standalone=no>
<main>
<ToDo time="1">
<Item priority="1"> Go to the <bold>Toy store!</bold></Item>
<Item priority="2"> Do bills</Item>
</ToDo>
<ToDo time="2">
<Item priority="1"> Go to the Second<bold>Toy store!</bold></Item>
</ToDo>
</main>
Here is my code
TiXmlDocument doc("new.xml");
TiXmlNode * element=doc.FirstChild("main");
element=element->FirstChild("ToDo");
string temp=static_cast<TiXmlElement *>(element)->Attribute("time");
But I am getting run time errors from the third and fourth lines. Can anybody shed a light on this isssue?
It seems to me that you forgot to load the file. Normally I do something along these lines:
TiXmlDocument doc("document.xml");
bool loadOkay = doc.LoadFile(); // Error checking in case file is missing
if(loadOkay)
{
TiXmlElement *pRoot = doc.RootElement();
TiXmlElement *element = pRoot->FirstChildElement();
while(element)
{
string value = firstChild->Value(); // In your example xml file this gives you ToDo
string attribute = firstChild->Attribute("time"); //Gets you the time variable
element = element->NextSiblingElement();
}
}
else
{
//Error conditions
}
Hope this helps
#include "tinyXml/tinyxml.h"
const char MY_XML[] = "<?xml version='1.0' standalone=no><main> <ToDo time='1'> <Item priority='1'> Go to the <bold>Toy store!</bold></Item> <Item priority='2'> Do bills</Item> </ToDo> <ToDo time='2'> <Item priority='1'> Go to the Second<bold>Toy store!</bold></Item> </ToDo></main>";
void main()
{
TiXmlDocument doc;
TiXmlHandle docHandle(&doc);
const char * const the_xml = MY_XML;
doc.Parse(MY_XML);
TiXmlElement* xElement = NULL;
xElement = docHandle.FirstChild("main").FirstChild("ToDo").ToElement();
int element_time = -1;
while(xElement)
{
if(xElement->QueryIntAttribute("time", (int*)&element_time) != TIXML_SUCCESS)
throw;
xElement = xElement->NextSiblingElement();
}
}
That's how it works. Compiled & tested.
As you can see your tries to make it extra-safe code cost you with an exceotion at your third line (of the question), and without testing I can bet it's a "pointing-to-null" exception.
Just load it my style, as TinyXml's docs say as well: "docHandle.FirstChild("main").FirstChild("ToDo").ToElement();".
Hope it helps you understand, let me know if it's not clear. I accept visa (:
Is it just me or the the pugixml version looks much better?
#include <iostream>
#include "pugixml.hpp"
using namespace std;
using namespace pugi;
int main()
{
xml_document doc;
if (!doc.load_file("new.xml"))
{
cerr << "Could not load xml";
return 1;
}
xml_node element = doc.child("main");
element = element.child("ToDo");
cout << "Time: " << element.attribute("time") << endl;
}
Also new.xml had an error, instead of:
<?xml version="1.0" standalone=no>
should be
<?xml version="1.0" standalone="no"?>
Compilation was just a matter of cl test.cpp pugixml.cpp