Reading XML data using boost::property_tree library functions in C++ - c++

<?xml version="1.0"?>
<sked>
<version>2</version>
<flight xmlns:xsi="some_uri" xsi:type="emirates">
<carrier>BA</carrier>
<number>4001</number>
<date>2011-07-21</date>
</flight>
<flight xmlns:xsi="some_uri" xsi:type="cathey-pacific">
<flight_class>
<type>Economy</type>
<fare>400</fare>
</flight_class>
<date>2011-07-21</date>
</flight>
</sked>
I have a XML document which describe 2 types of flights by same keywords. Sub fields are depend on the type of the flight. I have to read the XML and store it into C++ data classes according to the type of flights.
This is my code segment that is used for this purpose.
typedef boost::property_tree::ptree Node;
Node pt;
read_xml(test.xml, pt);
Node skedNode = pt.get_child("sked");
Node flightNode = skedNode.get_child("flight");
BOOST_FOREACH(Node::value_type const& v, skedNode.get_child("sked"))
{
if (v.first == "flight")
{
if (v.second.get("<xmlattr>.xsi:type", "Null") == "cathey-pacific")
{
BOOST_FOREACH(Node::value_type const& v1, flightNode.get_child("flight"))
{
if(v1.first == "flight_class")
FlightClass fclass = FlightClass(static_cast<Node>(flightNode));
}
}
}
}
When I try to run the above code, I got nothing inside FlightClass. I tried to debug the above code and found, v1.first is getting the values "carrier", "number" and "value" only.
I surprised because, those are the parameter of emirates type of flights. I couldn't receive cathey-pacific flight information.
Please help me to find out what the issue is.
I really want to get the information of cathy-pacific flights from this XML file and store into C++ data classes.
What should I do to correct this?
Note: Instead of second BOOST_FOREACH, I tried v.second.get_child("flight"); but it's throwing an exception. Then I replaced above by v.second.get_child("flight_class"); and it's giving it's sub-fields like: type and fare.
What may be the reason for that? It seems it's returning its grandchild nodes.

Boost doesn't provide any functionality like "get_next_child" to get different child nodes if there are more than one children nodes with same name.
So I just removed the unwanted fields from the tree before I iterate for above purpose.
flightNode.pop_front(); // to remove xmlattr
flightNode.pop_front(); // to remove version field
flightNode.pop_front(); // to remove first flight field.
Then used BOOST_FOREACH to reach above goal.

Related

How to Create a Child with pair tag with QT

I am working on a project where I have to write information in a XML document using QT Library ( QTDomDocument, etc.) for C++.
It might be stupid to ask, but I am looking for a solution on how to create a XML child node with pair tags, for example, I have this :
<color_space>
</color_space>
And I would like to add x childs following this schema:
<color_space>
<color_plan>R</color_plan>
<color_plan>G</color_plan>
...
</color_space>
I have tried multiple different codes and as far I did, the most similar resultat that I had was :
<color_space>
<color_plan/>
<color_plan/>
...
</color_space>
Thank you all for your help.
Ok so I Just Figured out how to do what I attempted to do, I hope it will help other people!
// Here we have a QVector where we have all our color plan
QVector<QString>ColorPlanTable= { "R","G","B","L","a","b","Y","Cb","Cr" };
//sub_element is the Color_space element (parent node)
sub_element= sub_element.nextSiblingElement();
// Here we create a new element for color_plan (child of Color_space)
QDomElement NewColorPlan=document_->createElement("color_plan");
// And then we create a TextNode to add to the element color_plan
QDomText NewColorPlanText = document_->createTextNode(ColorPlanTable.at(i));
// Adding the element color_plan as a child of Color_space
sub_element.appendChild(NewColorPlan);
// And then adding the text of the color_plan!
NewColorPlan.appendChild(NewColorPlanText);

XQuery to append node to existing node set

I am new to working with XQuery and XSLT. WebLogic is using XQuery 1.0.
I have a WebLogic composite and I have a collection of nodes and just want to add a node to an existing node set in an XQuery assignment. My current solution seems too heavyweight. I will be dealing with tens of thousands of records and it seems to me there should be a really easy way to just insert a node under another node and return it.
declare function local:func($svdPpl as element() (:: element(*, sv:svdPpl) ::),
$svdPsn as element() (:: element(*, sv:svdPsn) ::))
as element() (:: element(*, sv:svdPpl) ::) {
<sv:svdPpl>
{for $psn in $svdPpl return $psn} {$svdPsn}
</sv:svdPpl>
};
If I understand XQuery correctly that means millions of loops just to add records to existing node sets. I have tried using the insert node $svdPsn into $svdPpl syntax but that is causing validation errors in the WebLogic editor.
Any help is appreciated.
XQuery is a functional language and variables are immutable. It's not possible to change the value of a variable once an assignment has been made using standard XQuery.
There is an extension to XQuery that allows updates called XQuery Update Facility, but not all processors implement it. I'm not familiar with WebLogic, but it looks like some versions support it. That would be the direct route.
Failing that, a common pattern is to write a generic recursive typeswitch function that walks the tree and applies transformation logic as it generates a modified copy of the input. This is how things work implicitly in XSLT. It's unclear from your question exactly how that would be implemented, but just as an example:
declare function local:add-after(
$context as item(),
$target-name as xs:QName,
$payload as item()*
) as item()*
{
typeswitch ($context)
case element() return (
element { node-name($context) } {
$context/#*,
for $n in $context/node()
return local:add-after($n, $target-name, $payload)
},
if (node-name($context) = $target-name)
then $payload
else ())
default return $context
};
Given an XML node $xml with a value of:
<x>
<y>
<z></z>
</y>
</x>
You could use the above function local:add-after($xml, xs:QName('y'), <payload/>) to add a payload after the <y> element:
<x>
<y>
<z></z>
</y>
<payload></payload>
</x>

Parse XML with unknown elements in C++ and Qt

I've got an XML document that I receive from a REST service that I wish to parse. The service might change their element and tag names so I'm trying to come up with some completely generic solution that saves the document structure as some kind of object with attributes.
I'm not sure how/if the C++ and Qt API's allow me to accomplish such a thing however. I've thought of creating some kind of keyed map that can hold element names as string keys and values would be their children in some recursive fashion. Being very new to Qt and C++ I'm not sure how I can accomplish this.
This could be an example XML document:
<root>
<element id="1">
<name>SomeName</name>
<desc>SomeDesc</desc>
<params>
<param pid="1">True</param>
<param pid="2">False</param>
</params>
...
<Some unknown element></Some unknown element>
</element>
</root>
This is how I convert the HTTP response (QNetworkReply) to a DOM document I can use in Qt:
QByteArray data = reply->readAll();
QDomDocument doc;
doc.setContent(data);
QDomNodeList nodes = doc.elementsByTagName("root");
if (nodes.size() > 0) {
// Prints all elements, should be able to save in a map somehow? Perhaps there is a better way?
qDebug() << nodes.at(0).toElement().text();
}
I would love some input on how I can parse this in a way that allows me to keep all information in the XML even without knowing the element, attribute and tag names. Something like this:
element = {
id : 1,
name : 'SomeName',
desc : 'SomeDesc',
params : [{
pid : 1,
param : True
}, {
pid : 2,
param : False
}
],
some unknown element : some unknown values
}

XML Getting attibute value of a Node

I am using XML DOM API in C++ to parse an XML file. I can't find any method to get the attribute value in a node element.
For example, in the following xml
<test>
<fruit count="10">
...
...
</fruit>
<test>
I need to get the count string("10") using XML APIs. Can anybody help me with some code snippets.
Use DOM Parser API to get attribute value count.
Refer below sample code:
//code to perform some process for parsing the input file and get rootElement
DOMNodeList* fruitNodes= rootElement->getElementsByTagName(XMLString::transcode("fruit"));
DOMNode* node = fruitNodes->item(0);
DOMElement* fruitElement = dynamic_cast <xercesc::DOMElement*>(node);
const XMLCh* attrValue = fruitElement->getAttribute(XMLString::transcode("count"));
you can get value 10 from attrValue using: string(XMLString::transcode(attrValue))
Based on http://msdn.microsoft.com/en-us/library/windows/desktop/ms754523(v=vs.85).aspx
Try something like:
pXMLDomNodeList = pXMLDocElement->selectNodes("/test/fruit/#count");

C++ Boost Property Tree Update Existing Node By Attribute Qualifier

Ok, so here's a sample of the XML structure:
<config>
<Ignored>
<Ignore name="Test A">
<Criteria>
<value>actual value</value>
</Criteria>
</Ignore>
<Ignore name="Test B">
<Criteria>
<value>actual value</value>
</Criteria>
</Ignore>
</Ignored>
<config>
I would like to be able to do two things:
Perform a get directly to the Test A element without having to loop all Ignore elements..like a selector on an attribute.
If nothing else, I need a method of updating either of the Ignore elements and can't seem to figure it out
Do I have to delete the element and recreate it? I can't seem to figure out a way to perform a put which qualifies an element (where there a many with the same name at the same level) by an attribute (which would be unique at that level).
Something like:
pt.put("config.Ignored.Ignore.<xmlattr>.name='Test A'.Criteria.value",some_var)
Or anything else that can achieve the end goal. Thank you very much!
Full disclosure: I'm pretty new to C++ and may be missing something blatantly obvious.
Boost.property_tree xml parser (RapidXML) doesn't support this.
Consider using something like TinyXPath is you want such functionality out of the box.
Or use explicit loop to find Ignore node with required attribute. Then you can use
someIgnoreNode.put("Criteria.value", some_var);
you may use a method like:
auto & pt_child = pt.getchild("config.Ignored");
BOOST_FOREACH(ptree::value_type &v1, pt_child)
{
if (v1.first == Ignore && v1.second.get<std::string>("<xmlattr>.name") == "Test A")
{
ptree & ptGrandChild = v1.second;
ptGrandChild.put<std::string>("Criteria.value", some_var);
}
}
boost::property_tree::xml_writer_settings<std::string> settings =
boost::property_tree::xml_writer_make_settings<std::string>('\t', 1);
write_xml(xmlPath, pt, std::locale(), settings);