Getting the value from a leaf node - c++

I have the following XML subtree:
<xdm:Device xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bsi="http://www.hp.com/schemas/imaging/con/bsi/2003/08/21" xmlns:dd="http://www.hp.com/schemas/imaging/con/dictionaries/1.0/" xmlns:pwg="http://www.hp.com/schemas/imaging/con/pwg/sm/1.0/" xmlns:xdm="http://www.hp.com/schemas/imaging/con/xdm/1.1/" xmlns:alerts="http://www.hp.com/schemas/imaging/con/alerts/2006/01/13" xmlns:count="http://www.hp.com/schemas/imaging/con/counter/2006/01/13" xmlns:job="http://www.hp.com/schemas/imaging/con/job/2006/01/13" xmlns:media="http://www.hp.com/schemas/imaging/con/media/2006/01/13" xmlns:icd="http://www.hp.com/schemas/imaging/icd/2006/08/27" language="en-US">
<xdm:Information>
<dd:ModificationNumber>
12
</dd:ModificationNumber>
</xdm:Information>
</xdm:Device>
And I want to get the value of the dd:ModificationNumber. For that, I use the following XPath:
/xdm:Device/xdm:Information/dd:ModificationNumber
And this code:
TiXmlDocument xmlDoc;
xmlDoc.Parse(xmlSubtree, 0, TIXML_ENCODING_UTF8);
TiXmlElement* xmlElement = xmlDoc.FirstChildElement();
xpath_processor xpathProcessor(xmlElement, xPath);
expression_result expressionResult = xpathProcessor.er_compute_xpath();
int modificationNumber = expressionResult.i_get_int();
However, the resultant expression refers to the root node of the tree, and not to an int as I expected. And the modificationNumber is zero. Any ideas?

Related

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;
}

TinyXML2 - insert element in middle of XML

I'm looking to add new elements with data to the middle of my XML structure. How can I append them where I need them?
Current code:
XMLElement *node = doc.NewElement("timeStamp");
XMLText *text = doc.NewText("new time data");
node->LinkEndChild(text);
doc.FirstChildElement("homeML")->FirstChildElement("mobileDevice")->FirstChildElement("event")->LinkEndChild(node);
doc.SaveFile("homeML.xml");
And an example part of my XML structure:
<mobileDevice>
<mDeviceID/>
<deviceDescription/>
<units/>
<devicePlacement/>
<quantisationResolution/>
<realTimeInformation>
<runID/>
<sampleRate/>
<startTimeStamp/>
<endTimeStamp/>
<data/>
</realTimeInformation>
<event>
<mEventID/>
<timeStamp/>
<data/>
<support/>
</event>
</mobileDevice>
I'm looking to add it addtional timeStamp tags under mobileDevice->event between mEventID and data, at the moment they are being appended after the support tag how can I get them to be entered in the correct place?
Current placement when ran:
<mobileDevice>
<mDeviceID/>
<deviceDescription/>
<units/>
<devicePlacement/>
<quantisationResolution/>
<realTimeInformation>
<runID/>
<sampleRate/>
<startTimeStamp/>
<endTimeStamp/>
<data/>
</realTimeInformation>
<event>
<mEventID/>
<timeStamp/>
<data/>
<support/>
<timeStamp>new time data</timeStamp>
</event>
</mobileDevice>
You want to use InsertAfterChild() to do this. Here's an example which should do what you want (assuming that "mobileDevice" is your document's root element):
// Get the 'root' node
XMLElement * pRoot = doc.FirstChildElement("mobileDevice");
// Get the 'event' node
XMLElement * pEvent = pRoot->FirstChildElement("event");
// This is to store the element after which we will insert the new 'timeStamp'
XMLElement * pPrecedent = nullptr;
// Get the _first_ location immediately before where
// a 'timeStamp' element should be placed
XMLElement * pIter = pEvent->FirstChildElement("mEventID");
// Loop through children of 'event' & find the last 'timeStamp' element
while (pIter != nullptr)
{
// Store pIter as the best known location for the new 'timeStamp'
pPrecedent = pIter;
// Attempt to find the next 'timeStamp' element
pIter = pIter->NextSiblingElement("timeStamp");
}
if (pPrecedent != nullptr)
{
// Build your new 'timeStamp' element,
XMLElement * pNewTimeStamp = xmlDoc.NewElement("timeStamp");
pNewTimeStamp->SetText("Your data here");
// ..and insert it to the event element like this:
pEvent->InsertAfterChild(pPrecedent, pNewTimeStamp);
}
This is an interesting and probably common use case. I wrote a TinyXML2 tutorial a couple of months ago, so I'll add this to it.

Rapidxml and start tags

I am having a few problems with rapidxml. When I compile my code no start tags are included..any and all help will be greatly appreciated
int main(){
xml_document<> doc;
//xml declaration
xml_node<>* decl = doc.allocate_node(node_declaration);
decl->append_attribute(doc.allocate_attribute("version","1.0"));
decl->append_attribute(doc.allocate_attribute("encoding", "utf-8"));
doc.append_node(decl);
// root node
xml_node<>* root = doc.allocate_node(node_element, "root");
root->append_attribute(doc.allocate_attribute(""));
doc.append_node(root);
// child/sibling node
xml_node<>* sibling0 = doc.allocate_node(node_element, "old");
root->append_node(sibling0);
xml_node<>* sibling = doc.allocate_node(node_element,"boy");
root->append_node(sibling );
std::string xmlstring;
print(back_inserter(xmlstring), doc);
cout << xmlstring << endl;}
Apart from the necessary #includes, and missing using namespace rapidxml; your code compiles and runs fine for me.
xmlstring contains this:
<?xml version="1.0" encoding="utf-8"?>
<root ="">
<old/>
<boy/>
</root>
Because old and boy have no content, they use xml empty element tags, <tagname/>rather than start and end pairs like <tagname></tagname>

how to write data TINYXML2 on IOS

My test.xml like this:
<?xml version="1.0"?>
<!DOCTYPE PLAY SYSTEM "play.dtd">
<data>
<CurrentLevel>5</CurrentLevel>
<BestScoreLV1>1</BestScoreLV1>
<BestScoreLV2>2</BestScoreLV2>
</data>
<dict/>
My Code here:
std::string fullPath = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath("text.xml");
tinyxml2::XMLDocument doc;
doc.LoadFile(fullPath.c_str());
tinyxml2::XMLElement* ele = doc.FirstChildElement("data")->FirstChildElement("BestScoreLV2")->ToElement();
ele->SetAttribute("value", 10);
doc.SaveFile(fullPath.c_str());
const char* title1 = doc.FirstChildElement("data")->FirstChildElement("BestScoreLV2")->GetText();
int level1 = atoi(title1);
CCLOG("result is: %d",level1);
But value of BestScoreLV2 when output is also 2. How can I change and write data to XML?
In TinyXML2 text is represented by XMLText class which is child of XMLNode class.
XMLNode have methods Value() and SetValue() which have different meanings for different XML nodes.
For text nodes Value() read node's text and SetValue() write it.
So you need code like this:
tinyxml2::XMLNode* value = doc.FirstChildElement("data")->
FirstChildElement("BestScoreLV2")->FirstChild();
value->SetValue("10");
The first child of BestScoreLV2 element is XMLText with value 2. You change this value to 10 by calling SetValue(10).

Why is this loop only running once?

Why is this loop only running once?
noteDatabaseItem just takes a node and fills in the data. the xml has 3 notes in it.
XML:
<?xml version="1.0" encoding="utf-8"?>
<noteCollection>
<note name="Test Note 1">This is test note 1 content!</note>
<note name="Test Note 2">This is test note 2 content!</note>
<note name="Test Note 3">This is test note 3 content!</note>
</noteCollection>
C++:
std::vector<notekeeper::noteDatabaseItem> noteList;
TiXmlElement* noteCollection = xmlDoc->FirstChildElement("noteCollection");
TiXmlElement* node = noteCollection->FirstChildElement("note");
int itemCount = 0;
while (node != NULL) {
itemCount++;
noteList.resize(itemCount);
noteList.push_back(noteDatabaseItem(node));
node = noteCollection->NextSiblingElement("note");
}
Shouldn't it be node = node->NextSiblingElement("note")?
noteCollection has only children, not siblings, right?
You're getting the wrong element in your loop. Try this:
while (node != NULL) {
itemCount++;
noteList.push_back(noteDatabaseItem(node));
node = node->NextSiblingElement("note");
}
The next sibling of the current node is the one you want. You were trying to get the next sibling of the parent node.
node = noteCollection->NextSiblingElement("note");
is meant to be
node = node->NextSiblingElement("note");
Stupid mistake. Sibling not Child.