Parse XML using pugixml - c++

I am trying to fetch, internet time using a web service, which provides me an xml. Now, I am trying to parse the xml file using pugixml. The XML returned
<?xml version="1.0" encoding="ISO-8859-1" ?>
<timezone xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.earthtools.org/timezone.xsd">
<version>1.0</version>
<location>
<latitude>22.5667</latitude>
<longitude>88.3667</longitude>
</location>
<offset>5.5</offset>
<suffix>E*</suffix>
<localtime>20 Jul 2014 14:48:10</localtime>
<isotime>2014-07-20 14:48:10 +0530</isotime>
<utctime>2014-07-20 09:18:10</utctime>
<dst>Unknown</dst>
</timezone>
The way I am trying to parse it.
pugi::xml_document doc;
if (!doc.load_file("time.xml")) return -1;
pugi::xml_node tools = doc.child("timezone").child("localtime");
//[code_traverse_iter
for (pugi::xml_node_iterator it = tools.begin(); it != tools.end(); ++it)
{
std::cout << "Tool:";
for (pugi::xml_attribute_iterator ait = it->attributes_begin(); ait != it->attributes_end(); ++ait)
{
std::cout << " " << ait->name() << "=" << ait->value();
}
std::cout << std::endl;
}
return 0;
I need fetch the value of this node
<localtime>20 Jul 2014 14:48:10</localtime>
Please help me get through this.
P.S: The web-service I am using can be found http://www.earthtools.org/webservices.htm, hope it helps someone.
I know I can do a simple file operation to fetch the data as the xml is not that long, but still I would like to use the parser.

const char* localtime = doc.child("timezone").child("localtime").text().get();
or
const char* localtime = doc.child("timezone").child_value("localtime");

Related

How to correctly access xml attributes using pugixml?

I'm new to xml, and using the pugixml documentation I haven't been able to read a node attribute to the console. The core of the issue is that I don't understand how the node accessing structure works and I haven't been able to figure it out using my code.
The code provided is a lightly modified line from the actual pugixml manual, so I'm not sure why it isn't working.
XML FILE LOADED
<?xml version="1.0" encoding="utf-8"?>
<settings description="settings root description">
<!--settings for the options menu-->
<options description="game options">
<resolution description="resolution in 16:9 ratio">6</resolution>
<volume description="volume setting">0</volume>
</options>
</settings>
C++ CODE TRYING TO USE XML FILE
//set up and load settings xml doc
pugi::xml_document settingsXML;
pugi::xml_parse_result settingsResult = settingsXML.load_file("SFMLVania/include/Settings.xml");
std::cout << "Root description: " << settingsXML.root().attribute("description").as_string();
I'm expecting to see:
"Root description: settings root description"
in the console.
Instead, I'm getting:
"Root description: "
SECOND ATTEMPT -- to try and just get any data and find out where I am in the tree:
std::cout << "Second attempt: " << settingsXML.first_attribute().as_string();
All I got from the second attempt was the console spitting out: "Second attempt: "
It turns out, my load path didn't exist, I had forgotten to use ../ to go up a directory and out of the Debug folder where the .exe was stored. I figured this out by testing the load result that could be passed as a bool.
if (settingsResult)
{
std::cout << "returned true\n";
}
else
std::cout << "returned false\n";
if (!settingsResult)
{
std::cout << "also returned false\n";
}
I included some redundant returns in case the bool value didn't function as expected.
The result showed that there was no file loaded (it returned false), and when I reexamined my file structure and added the ../ as a test to the file path, it returned true.
For anyone that comes across this, you can prevent this error by adding a minimal parse check after the assigning of settingsResult like this:
pugi::xml_document settingsXML;
pugi::xml_parse_result settingsResult = settingsXML.load_file("SFMLVania/include/Settings.xml");
// Catch parsing error
if (!settingsResult )
{
std::cout << "ERROR: " << settingsResult.description() << '\n';
return -1;
}
This will catch a bad parse and log the error to the console.

Xerces XPath causes seg fault when path doesn't exist

I can successfully use Xerces XPath feature to query for information from an XML with the following XML and C++ code.
XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
<ApplicationSettings>
hello universe
</ApplicationSettings>
</root>
C++
int main()
{
XMLPlatformUtils::Initialize();
// create the DOM parser
XercesDOMParser *parser = new XercesDOMParser;
parser->setValidationScheme(XercesDOMParser::Val_Never);
parser->parse("fake_cmf.xml");
// get the DOM representation
DOMDocument *doc = parser->getDocument();
// get the root element
DOMElement* root = doc->getDocumentElement();
// evaluate the xpath
DOMXPathResult* result=doc->evaluate(
XMLString::transcode("/root/ApplicationSettings"), // <-- HERE IS THE XPATH
root,
NULL,
DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, //DOMXPathResult::ANY_UNORDERED_NODE_TYPE, //DOMXPathResult::STRING_TYPE,
NULL);
// look into the xpart evaluate result
result->snapshotItem(0);
std::cout<<TranscodeToStr(result->getNodeValue()->getFirstChild()->getNodeValue(),"ascii").str()<<std::endl;;
XMLPlatformUtils::Terminate();
return 0;
}
The problem is that sometimes my XML will only have certain fields. But if I remove the ApplicationSettings entry from the XML it will seg fault. How can I properly handle these optional fields? I know that trying to correct from seg faults is risky business.
The seg fault is occurring in this line
std::cout<<TranscodeToStr(result->getNodeValue()->getFirstChild()->getNodeValue(),"ascii").str()<<std::endl;
specifically in get getFirstChild() call because the result of getNodeValue() is NULL.
This is my quick and dirty solution. It's not really ideal but it works. I would prefer a more sophisticated evaluation and response.
if (result->getNodeValue() == NULL)
{
cout << "There is no result for the provided XPath " << endl;
}
else
{
cout<<TranscodeToStr(result->getNodeValue()->getFirstChild()->getNodeValue(),"ascii").str()<<endl;
}

Find Key in XML with Boost

I am using boost for the first time within an old code base that we have
iptree children = pt.get_child(fieldName);
for (const auto& kv : children) {
boost::property_tree::iptree subtree = (boost::property_tree::iptree) kv.second ;
//Recursive call
}
My problem is sometimes the fieldName doesn`t exist in the XML file and I have an exception
I tried :
boost::property_tree::iptree::assoc_iterator it = pt.find(fieldName);
but I dont know how to use the it I can`t use: if (it != null)
Any help please will be appreciated
I am using VS 2012
If it`s very complicated is there any other way to read a XML with nested nodes? I am working on that since 3 days
This is an Example of the XML
<?xml version="1.0" encoding="utf-8"?>
<nodeA xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<nodeA.1>This is the Adresse</nodeA.1>
<nodeA.2>
<node1>
<node1.1>
<node1.1.1>Female</node1.1.1>
<node1.1.2>23</node1.1.2>
<node1.1.3>Engineer</node1.1.3>
</node1.1>
<node1.2>
<node1.2.1>Female</node1.2.1>
<node1.2.2>35</node1.2.2>
<node1.2.3>Doctors</node1.2.3>
</node1.2>
</node1>
</nodeA.2>
<nodeA.3>Car 1</nodeA.3>
</nodeA>
Use pt.get_child_optional(...) to prevent an exception. pt.find(...) returns an iterator which compares true to pt.not_found() on failure.
EDIT: How to use boost::optional<--->
boost::optional< iptree & > chl = pt.get_child_optional(fieldname);
if(chl) {
for( auto a : *chl )
std::cerr << ":" << a.first << ":" << std::endl;
}

QXmlStreamReader says that i'm at the end of an XML file while in the middle of it

Hello the problem is that i'm reading a XML file using QXmlStreamReader to read in Appointments for my Calendar but when I'm switching from AppointmentSolo to AppointmentRepeat atEnd() returns true for some reason.
This is the XML file
<?xml version="1.0" encoding="UTF-8"?>
<AppointmentSolo>
<Length>1</Length>
<AppointmentSolo0>
<Date>10 09 2011</Date>
<Begin>15:11</Begin>
<End>23:12</End>
<Title>Final test</Title>
<Description>Final countdown</Description>
<hasNotify>1</hasNotify>
<notify>17</notify>
</AppointmentSolo0>
</AppointmentSolo>
<AppointmentRepeat>
<Length>1</Length>
<AppointmentRepeat0>
<Date>08 01 2014</Date>
<Begin>20:08</Begin>
<End>23:09</End>
<Type>MONTHLY</Type>
<Exceptions>
<Length>1</Length>
<Exception0>08 09 2014</Exception0>
</Exceptions>
<Title>Repeat test</Title>
<Description>FooBar</Description>
<hasNotify>0</hasNotify>
<notify>0</notify>
</AppointmentRepeat0>
</AppointmentRepeat>
And here is the part of my code which reads it and where the problem occurs.
if(Rxml.isEndElement() && Rxml.name() == "AppointmentSolo")
{
qDebug() << Rxml.atEnd() << Rxml.name() << Rxml.hasError();
Rxml.readNext();
qDebug() << Rxml.atEnd() << Rxml.name() << Rxml.hasError();
qDebug() << Rxml.error();
while(!Rxml.atEnd() && !Rxml.isStartElement())//om aan begin tag te zijn
{
Rxml.readNext();
qDebug() << Rxml.atEnd() << Rxml.name() << Rxml.hasError();
}
}
This is what is outputted
false "AppointmentSolo" false
true "AppointmentRepeat" true
3
It seems this is a QXmlStreamReader::NotWellFormedError
The parser internally raised an error due to the read XML not being well-formed.
But why is my XML not well formed then?
EDIT: it seems there happens an error (i added the "<< Rxml.hasError()")
XML can have only one root element, so your XML is invalid.

Parsin XML file using pugixml

Hi
I want to use XML file as a config file, from which I will read parameters for my application. I came across on PugiXML library, however I have problem with getting values of attributes.
My XML file looks like that
<?xml version="1.0"?>
<settings>
<deltaDistance> </deltaDistance>
<deltaConvergence>0.25 </deltaConvergence>
<deltaMerging>1.0 </deltaMerging>
<m> 2</m>
<multiplicativeFactor>0.7 </multiplicativeFactor>
<rhoGood> 0.7 </rhoGood>
<rhoMin>0.3 </rhoMin>
<rhoSelect>0.6 </rhoSelect>
<stuckProbability>0.2 </stuckProbability>
<zoneOfInfluenceMin>2.25 </zoneOfInfluenceMin>
</settings>
To pare XML file I use this code
void ReadConfig(char* file)
{
pugi::xml_document doc;
if (!doc.load_file(file)) return false;
pugi::xml_node tools = doc.child("settings");
//[code_traverse_iter
for (pugi::xml_node_iterator it = tools.begin(); it != tools.end(); ++it)
{
cout<<it->name() << " " << it->attribute(it->name()).as_double();
}
}
and I also was trying to use this
void ReadConfig(char* file)
{
pugi::xml_document doc;
if (!doc.load_file(file)) return false;
pugi::xml_node tools = doc.child("settings");
//[code_traverse_iter
for (pugi::xml_node_iterator it = tools.begin(); it != tools.end(); ++it)
{
cout<<it->name() << " " << it->value();
}
}
Attributes are loaded corectly , however all values are equals 0. Could somebody tell me what I do wrong ?
I think your problem is that you're expecting the value to be stored in the node itself, but it's really in a CHILD text node. A quick scan of the documentation showed that you might need
it->child_value()
instead of
it->value()
Are you trying to get all the attributes for a given node or do you want to get the attributes by name?
For the first case, you should be able to use this code:
unsigned int numAttributes = node.attributes();
for (unsigned int nAttribute = 0; nAttribute < numAtributes; ++nAttribute)
{
pug::xml_attribute attrib = node.attribute(nAttribute);
if (!attrib.empty())
{
// process here
}
}
For the second case:
LPCTSTR GetAttribute(pug::xml_node & node, LPCTSTR szAttribName)
{
if (szAttribName == NULL)
return NULL;
pug::xml_attribute attrib = node.attribute(szAttribName);
if (attrib.empty())
return NULL; // or empty string
return attrib.value();
}
If you want stock plain text data into the nodes like
<name> My Name</name>
You need to make it like
rootNode.append_child("name").append_child(node_pcdata).set_value("My name");
If you want to store datatypes, you need to set an attribute. I think what you want is to be able to read the value directly right?
When you are writing the node,
rootNode.append_child("version").append_attribute("value").set_value(0.11)
When you want to read it,
rootNode.child("version").attribute("version").as_double()
At least that's my way of doing it!