TinyXML2 - insert element in middle of XML - c++

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.

Related

How to change XML attribute value?

I want change an attribute value of a XML file.
I already change a node value, in this manner:
xercesc_3_2::XMLPlatformUtils::Initialize();
xercesc_3_2::XercesDOMParser* parser = new xercesc_3_2::XercesDOMParser;
parser->setValidationScheme(xercesc_3_2::XercesDOMParser::Val_Never);
parser->parse(fileName.c_str());
xercesc_3_2::DOMDocument* doc = parser->getDocument();
xercesc_3_2::DOMElement* root = doc->getDocumentElement();
xercesc_3_2::DOMXPathResult* result = doc->evaluate(
xercesc_3_2::XMLString::transcode("/document/child1/child2"),
root,
NULL,
xercesc_3_2::DOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
NULL);
result->getNodeValue()->getFirstChild()->setNodeValue(xercesc_3_2::XMLString::transcode(value.c_str()));
But I don't understand how to change the attribute value.
How can I do?

parsing comment in tinyXML2

I have problem with parsing XML comment. How can i properly access to comment?
Or is even possible to read comment with tinyXML2?
<xml>
<foo> Text <!-- COMMENT --> <foo2/></foo>
</xml>
I created
XMLElement *root = xmlDoc->FirstChildElement("foo");
XMLElement *child = root->FirstChildElement();
From child element i get foo2 element, What is propper way to read comment element from file.
Thanks
You can use XMLNode::FirstChild() and XMLNode::NextSibling() to loop through all child nodes. Use dynamic_cast to test if node is a comment.
if( const XMLElement *root = xmlDoc->FirstChildElement("foo") )
{
for( const XMLNode* node = root->FirstChild(); node; node = node->NextSibling() )
{
if( auto comment = dynamic_cast<const XMLComment*>( node ) )
{
const char* commentText = comment->Value();
}
}
}
I've made this up just from reading the documentation, so there might be mistakes in the code.
I just created a function on my project that navigates the entire document recursively and get rid of comments. You can use that to see how you can pick up any comment on the document... followed the example of the fellow above..
Code bellow:
// Recursively navigates the XML and get rid of comments.
void StripXMLInfo(tinyxml2::XMLNode* node)
{
// All XML nodes may have children and siblings. So for each valid node, first we
// iterate on it's (possible) children, and then we proceed to clear the node itself and jump
// to the next sibling
while (node)
{
if (node->FirstChild() != NULL)
StripXMLInfo(node->FirstChild());
//Check to see if current node is a comment
auto comment = dynamic_cast<tinyxml2::XMLComment*>(node);
if (comment)
{
// If it is, we ask the parent to delete this, but first move pointer to next member so we don't get lost in a NULL reference
node = node->NextSibling();
comment->Parent()->DeleteChild(comment);
}
else
node = node->NextSibling();
}
}

RapidXML - how can I handle missing nodes/values

I'd like to read from XML to C++ using RapidXML. However, if a node doen't exist or a value is missing the program crashes.
for (rapidxml::xml_node<> * xmlasset_node = root_node->first_node("Asset"); xmlasset_node; xmlasset_node = xmlasset_node->next_sibling())
{mystring += xmlasset_node->first_attribute("name")->value()};
However, this "name" attribute doesn't exist in all nodes and is to be filled with a default value, if its not in XML. Similar to this, I've got some sub-nodes not in all nodes. The reason is just to keep the XML as small and clear as possible for manual adjustments.
How can a check/test be implemented (C++), to prevent the program from crashing and just taking default values if a value/node doesn't exist?
Kind regards,
- Corak
Here is what I do, you can compare if the value of the node and its attribute matches your criteria then you accepts it:
// basically I am looking for "settings" node then "network" subnode, then "port" attribute
if( boost::iequals(doc.first_node()->next_sibling()->name(), "settings"))
{
for (xml_node<> *node = doc.first_node()->next_sibling()->first_node(); node; node = node->next_sibling())
{
// find network tag
if (boost::iequals(node->name(),"network"))
{
for (xml_attribute<> *attr = node->first_attribute(); attr; attr = attr->next_attribute())
{
if ( boost::iequals(attr->name(), "port"))
{
strcpy(attr->value(), portname);
}
}
}
}
}

Parsing <multi_path literal="not_measured"/> in TinyXML

How do I parse the following in TinyXML:
<multi_path literal="not_measured"/>
I am able to easily parse the below line:
<hello>1234</hello>
The problem is that the first statement is not getting parsed the normal way. Please suggest how to go about this.
Not 100% sure what youre question is asking but here is a basic format too loop through XML files using tinyXML:
/*XML format typically goes like this:
<Value atribute = 'attributeName' >
Text
</value>
*/
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(); //Gets the Value
string attribute = firstChild->Attribute("attribute"); //Gets the attribute
string text = firstChild->GetText(); //Gets the text
element = element->NextSiblingElement();
}
}
else
{
//Error conditions
}

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.