facing difficulty to reading complex xml file using libxml2 in c++ - c++

this is my xml file
<Example>
<text-frame name="SingleLineText 1" id="Text0" wrapping="true" direction="" suppress- empty-lines="false">
<style-ref name="default"/>
<style name="">
<alignment halign="left" valign="top"/>
<text-style text-color="Color 0" position="normal"/>
<font family-name="Times New Roman" size="14" style="Bold Italic"/>
<paragraph-style text-align="left"/>
</style>
<geometry size="absolute" position="absolute">
<location left="35" top="30"/>
<size width="165" height="45"/>
</geometry>
<condition name="" desc="">
<if>
<expression left="record1/#field3" operator="==" right="'1240 CrocoDoc Lane'"/>
<action-list>
<action type="suppress"/>
</action-list>
</if>
</condition>
<paragraph>
<style>
<paragraph-style indent-left="0" indent-right="0" indent-first="0" indent-hanging="0" line-spacing="multiple" line-spacing-at="1.2" text-align="left"/>
</style>
<text>
<style>
<font family-name="Times New Roman" size="14" style="Bold Italic"/>
<text-style text-color="Color 0" highlight="None" position="normal" character-spacing="0" case="normal"/>
</style>
<content><![CDATA[%(record1/#field3)]]></content>
</text>
</paragraph>
</text-frame>
</Example>
and my code to reading and writing is below.
what i want is to reach at the alignment tag using this and make change.. "/Example/text-frame/style/alignment" xpathexpression but its not working
xmlXPathObject * xpathObj = xmlXPathEvalExpression( (xmlChar*)"/Example/text-frame/style/alignment", xpathCtx );
//xmlXPathObject * xpathObj = xmlXPathEvalExpression( (xmlChar*)"/Example/Objects/Pet", xpathCtx );
if ( xpathObj == NULL ) throw "failed to evaluate xpath";
for(int i=0;i<=1; i++)
{
xmlNode *node = NULL;
if ( xpathObj->nodesetval && xpathObj->nodesetval->nodeTab )
{
node = xpathObj->nodesetval->nodeTab[i];
std::cout << "Found the node we want" << std::endl;
}
else
{throw "failed to find the expected node";}
xmlAttr *attr = node->properties;
while ( attr )
{
std::cout << "Attribute name: " << attr->name << " value: " << attr->children->content << std::endl;
attr = attr->next;
}
xmlSetProp( node, (xmlChar*)"age", (xmlChar*)"3" );
}

Related

boost::property_tree XML issue

I am fighting with getting values from the following XML structure:
<ActionMaps version="1" optionsVersion="2" rebindVersion="2" profileName="full_export">
<CustomisationUIHeader label="full_export" description="" image="">
<devices>
<keyboard instance="1"/>
<mouse instance="1"/>
</devices>
<categories>
<category label="#ui_CCSpaceFlight"/>
<category label="#ui_CGLightControllerDesc"/>
<category label="#ui_CCFPS"/>
<category label="#ui_CCEVA"/>
<category label="#ui_CGOpticalTracking"/>
<category label="#ui_CGInteraction"/>
</categories>
</CustomisationUIHeader>
<options type="keyboard" instance="1" Product="Tastatur {6F1D2B61-D5A0-11CF-BFC7-444553540000}">
<flight_move_yaw exponent="1.5"/>
</options>
<modifiers />
<actionmap name="spaceship_general">
<action name="v_close_all_doors">
<rebind input="kb1_0"/>
</action>
<action name="v_cooler_throttle_down">
<rebind input="kb1_6"/>
</action>
<action name="v_cooler_throttle_up">
<rebind input="kb1_5"/>
</action>
<action name="v_eject">
<rebind input="kb1_1"/>
</action>
...
The code I am using is
const std::string XML_PATH = std::string(fqn_file.mb_str());
boost::property_tree::ptree pt1;
boost::property_tree::read_xml( XML_PATH, pt1 );
// Traverse property tree example
BOOST_FOREACH(boost::property_tree::ptree::value_type const& node,
pt1.get_child( "ActionMaps.actionmap" ) ) {
boost::property_tree::ptree subtree = node.second;
if ( node.first == "actionmap" ) {
BOOST_FOREACH(boost::property_tree::ptree::value_type const& v,
subtree.get_child( "" ) ) {
std::string label = v.first;
if ( label != "<xmlattr>" ) {
std::string value = subtree.get<std::string>( label );
wxString m_value(value);
std::cout << label << ": " << value << std::endl;
wxString m_label(label);
wxLogMessage("DEBUG -> Label = " + m_label + " Value = " + value);
}
}
}
}
If I go for 'action' or 'label' the values are empty which seems to be normal. But how I can access name (#name) from action and input (#input) from rebind?
I expect to get e.g. the pair of values 'v_close_all_doors' and 'kb1_0'. Of course I need a second iteration for the second value but for the first I need to understand how to access these values.
I gave up with boost::property_tree as its seems to be designed to handle simple XML structures only. Made more progress using RapidXML

Parsing nested xml with boost

I have an xml which I like to read into my program, I've seen a lot of exmaples how to read xml using property tree from boost, however I couldn't make it work for the nested xml that I have:
<?xml version="1.0" encoding="UTF-8"?>
<document version="1.3.0">
<chunk>
<sensors>
<sensor id="0" label="unknown" type="frame">
<resolution width="2056" height="2464"/>
<property name="fixed" value="0"/>
<calibration type="frame" class="adjusted">
<resolution width="2056" height="2464"/>
<fx>7349.85579147491</fx>
<fy>7349.85579147491</fy>
<cx>1028</cx>
<cy>1232</cy>
<p1>0.000308132854297239</p1>
<p2>-0.000521332855614243</p2>
</calibration>
</sensor>
</sensors>
<cameras>
<camera id="0" label="img0000.png" sensor_id="0" enabled="1">
<transform>1.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 -1.0000000000000000e+000 -1.2246467991473532e-016 0.0000000000000000e+000 0.0000000000000000e+000 1.2246467991473532e-016 -1.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 1.0000000000000000e+000</transform>
</camera>
<camera id="1" label="img0011.png" sensor_id="0" enabled="1">
<transform>9.8183676675341047e-001 -2.7892274662900951e-002 -1.8766615162393202e-001 1.3502780959894856e+000 -2.8076662610258110e-002 -9.9960436765543659e-001 1.6760611099915072e-003 -8.8020303958543274e-003 -1.8763865398120155e-001 3.6234208013954891e-003 -9.8223144235654503e-001 -1.1015316085201440e-001 0.0000000000000000e+000 0.0000000000000000e+000 0.0000000000000000e+000 1.0000000000000000e+000</transform>
</camera>
</cameras>
<reference>LOCAL_CS["Local Coordinates (m)",LOCAL_DATUM["Local Datum",0],UNIT["metre",1,AUTHORITY["EPSG","9001"]]]</reference>
<region>
<center>-5.1216167685514069e-002 -7.0600760442627708e-001 -6.9904047240845895e+000</center>
<size>2.1074647950629393e+000 1.5533586459855240e+000 1.0253878730038792e+000</size>
<R>-9.6011547075389880e-001 2.7340714563038887e-001 5.8539008680217816e-002 -2.6221584379471408e-001 -9.5313222127937347e-001 1.5093647677772853e-001 9.7062526662174770e-002 1.2956659089939432e-001 9.8680867671533157e-001</R>
</region>
<settings>
<property name="accuracy_tiepoints" value="1"/>
<property name="accuracy_cameras" value="10"/>
<property name="accuracy_cameras_ypr" value="2"/>
<property name="accuracy_markers" value="0.005"/>
<property name="accuracy_scalebars" value="0.001"/>
<property name="accuracy_projections" value="0.1"/>
</settings>
</chunk>
</document>
I'm only interested in reading the cameras node and their attributes, and I've used the following code for that, but it doesn't work:
using namespace std;
using namespace boost;
using namespace boost::property_tree;
const ptree& empty_ptree() {
static ptree t;
return t;
}
int main()
{
ptree tree;
read_xml(XML_PATH1, tree);
tree = tree.get_child("document.chunk", empty_ptree());
const ptree & formats = tree.get_child("cameras.camera", empty_ptree());
BOOST_FOREACH(const ptree::value_type & f, formats)
{
string at = f.first + ".<xmlattr>";
const ptree & attributes = f.second.get_child("<xmlattr>", empty_ptree());
cout << "Extracting attributes from " << at << ":" << endl;
BOOST_FOREACH(const ptree::value_type &v, attributes)
{
cout << "First: " << v.first.data() << " Second: " << v.second.data() << endl;
}
}
}
so, for each camera you want to iterate over each attribute, don't you ?
if yes, then you need to use equal_range() instead of get_child(); the latter returns a single child, the former a range of associative iterators pointing to all childs with the given key.
so, use get_child() to get document.chunk.cameras, use equal_range('camera') to get all cameras and for each camera use equal_range('xmlattr') to traverse its attributes.

Why does this code produce 4 times nothing and the fifth time the correct data?

I got an XML-file:
<weatherdata>
<location>
<name>Vlaardingen</name>
<type/>
<country>NL</country>
<timezone/>
<location altitude="0"
latitude="51.912498"
longitude="4.34167"
geobase="geonames"
geobaseid="2745467"/>
</location>
<credit/>
<meta>
<lastupdate/>
<calctime>0.0152</calctime>
<nextupdate/>
</meta>
<sun rise="2016-02-23T06:40:58"
set="2016-02-23T17:11:47"/>
<forecast>
<time day="2016-02-23">
<symbol number="500"
name="lichte regen"
var="10d"/>
<precipitation/>
<windDirection deg="316"
code="NW"
name="Northwest"/>
<windSpeed mps="9.01"
name="Fresh Breeze"/>
<temperature day="6.06"
min="5.57"
max="6.06"
night="5.66"
eve="5.57"
morn="6.06"/>
<pressure unit="hPa"
value="1027.72"/>
<humidity value="96"
unit="%"/>
<clouds value="clear sky"
all="8"
unit="%"/>
</time>
<time day="2016-02-24">
<symbol number="501"
name="matige regen"
var="10d"/>
<precipitation value="3.15"
type="rain"/>
<windDirection deg="283"
code="WNW"
name="West-northwest"/>
<windSpeed mps="6.21"
name="Moderate breeze"/>
<temperature day="4.98"
min="4.17"
max="5.11"
night="4.17"
eve="4.85"
morn="4.32"/>
<pressure unit="hPa"
value="1030.97"/>
<humidity value="100"
unit="%"/>
<clouds value="scattered clouds"
all="48"
unit="%"/>
</time>
<time day="2016-02-25">
<symbol number="500"
name="lichte regen"
var="10d"/>
<precipitation value="1.23"
type="rain"/>
<windDirection deg="295"
code="WNW"
name="West-northwest"/>
<windSpeed mps="5.71"
name="Moderate breeze"/>
<temperature day="5.43"
min="4.92"
max="5.48"
night="5.34"
eve="5.48"
morn="4.92"/>
<pressure unit="hPa"
value="1026.18"/>
<humidity value="100"
unit="%"/>
<clouds value="broken clouds"
all="68"
unit="%"/>
</time>
</forecast>
</weatherdata>
This is my C++ code which reads the XML-file:
#include <iostream>
#include <string>
#include "tinyxml2.h"
using namespace std;
struct weatherData
{
// date of day
string time_day;
// symbol data for weathericon and display of weather type
string symbol_number;
string symbol_name;
string symbol_var;
// windspeed
string windSpeed_mps;
// min. and max. temperature
string temp_min;
string temp_max;
};
int main()
{
weatherData forecast[3];
int counter = 0;
tinyxml2::XMLDocument doc;
if(doc.LoadFile("daily.xml") == tinyxml2::XML_SUCCESS)
{
tinyxml2::XMLElement* root = doc.FirstChildElement();
for(tinyxml2::XMLElement* elem = root->FirstChildElement(); elem != NULL; elem = elem->NextSiblingElement())
{
std::string elemName = elem->Value();
for (tinyxml2::XMLElement* e = elem->FirstChildElement("time"); e != NULL; e = e->NextSiblingElement("time"))
{
if (e)
{
const char *time = e->Attribute("day");
forecast[counter].time_day = time;
counter++;
}
}
cout << "Time dates: " << endl;
for (int i = 0; i < 3;i++)
{
cout << forecast[i].time_day << endl;
}
counter = 0;
}
}
}
I am a novice in coding. I'm using the example code from a blog and adapted it for my needs. I know the for-loops just run across the elements in the XML-file.
And every time it finds the element 'time' it looks if it has an attribute 'day'. What I don't get is why it runs 4 times and the fifth time it produces the attributes of the three 'time' parts.
This is the output:
Time dates:
Time dates:
Time dates:
Time dates:
Time dates:
2016-02-23
2016-02-24
2016-02-25
It is because your outer loop iterates over all direct successors of root element weatherdata, i.e. it iterates over the element nodes location, credit, meta, sun, and forecast. For each of these elements, you search for the time-elements, in which you are actually interested. But the first 4 elements, i.e. location, credit, meta and sun, do not comprise any time-element, such that the first 4 iterations of the outer loop cannot extract any time data, whereas the 5th iteration then selects element node forecast, which has the three time-elements that you are looking for.
I suppose that it works if you change your code as follows (note the "forecast"-parameter in the call to FirstChildElement):
....
if(doc.LoadFile("daily.xml") == tinyxml2::XML_SUCCESS)
{
tinyxml2::XMLElement* root = doc.FirstChildElement();
for(tinyxml2::XMLElement* elem = root->FirstChildElement("forecast"); elem != NULL; elem = elem->NextSiblingElement())
{
....

Reading child node of a child node

Below i have show a normal XML file:
<Header>
<Sub Name="" Value="" /Sub>
<Sub Name="" value="" /Sub>
.
.
.
.
</Header>
I read the above mentioned XML like this:
QStringList Name;
QStringList value;
QXmlGet xmlget;
xmlget.load(Sample.xml);
xmlget.findAndDescend("Header");
while(xmlget.findNext("Sub")
{
Name.append(xmlget.getAttributeString("Name". "Unknown"));
value.append(xmlget.getAttributeString("value". "Unknown"));
}
xmlget.save(Sample.xml);
But the xml i have right now is bit complicated.
XML:
<Header>
<Sub Name= "" Value = ""><Sub1 Name = ""></Sub1></Sub>
<Sub Name= "" value = "" /Sub>
<Sub Name= "" Value = ""><Sub1 Name = ""></Sub1></Sub>
.
.
.
</Header>
Any suggestions how do I read the <Sub1>
Something like this one (Tested! It works!)
QStringList Name;
QStringList value;
QXmlGet xmlget;
xmlget.load("Sample.xml");
xmlget.findAndDescend("Header");
QXmlPut xmlPut(xmlget); // init reference from the variable - xmlget
while(xmlget.findNext("Sub")) {
Name.append(xmlget.getAttributeString("Name", "Unknown"));
value.append(xmlget.getAttributeString("Value", "Unknown"));
xmlget.descend(); // now Sub is the parent element
if( xmlPut.hasChildren() ) {
while(xmlget.findNext("Sub1") { // finding relatively the parent - Sub
xmlPut.goTo(xmlget.element());
xmlPut.setAttributeString("Name", "NAME1");
xmlPut.setAttributeString("Value", "VALUE1");
}
} else {
xmlPut.putInt("IntTag", "IntValue"); // We create the child node (IntTag) of the parent (Sub)
xmlPut.setAttributeString("AttrName", "AttrValue"); // and add some attributes to it
}
xmlget.rise();
}
xmlPut.save("Sample.xml"); // Any file name to save the xml data
docs

C++ If inside While

I want to parse some data from an XML Document, im using a while loop.
Poco::XML::NodeIterator it(_pDocument, Poco::XML::NodeFilter::SHOW_ELEMENT);
Poco::XML::Node* pNode = it.nextNode();
pNode = it.nextNode();
while (pNode)
{
std::cout<<pNode->nodeName()<<" :"<< pNode->nodeValue() << "\n";
if (pNode->nodeName() == "name")
{
std::cout << "Loading Map with Name : " << getString(pNode->nodeName()) << "\n";
}
if (pNode->nodeName() == "actor")
{
std::cout << "Actor Type : " << getString("actor[#type]") << "Actor Name : " << getString("actor[#name]") << "\n";
}
pNode = it.nextNode();
}
This works (albeit probably not the best solution). However, in my XML there are 2 actor elements with different values but the code returns the values of the first element twice.
edit:
<thorium>
<name>SampleLevel</name>
<actor type="1Volume" name="m_pActor1" enabled="true">
<attribute name="Rotation" value="-1.0 1.0 0.0"/>
<attribute name="Position" value="-1.2 0.0 0.0"/>
</actor>
<actor type="2Volume" name="m_pActor2" enabled="false">
<attribute name="Rotation" value="1.0 1.0 0.0"/>
<attribute name="Position" value="1.2 0.0 0.0"/>
</actor>
getString isn't using it or pNode, so it has no way to know whether you're looking at the first or the second occurrence. It looks like you're passing getString an XPath query. So it's returning whatever that query matches when executed on the entire document.
You probably want pNode->attributes