I'm trying to add an Element into an already existing Node/Parent like so:
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<BitComet Version="1.0">
<MyShares>
<bt title="Sound of Music" save_path="C:\Downloads|Sound of Music" task_finished="true" category="software" size="7126369" hash="31e0ded5b561ed698c151e72d5f20d9b75f03b12"/>
</MyShares>
</BitComet>
--
TiXmlDocument xml(pf);
if(!xml.LoadFile()) return;
TiXmlElement* root = xml.FirstChildElement("BitComet");
TiXmlElement* parent = xml.FirstChildElement("MyShares")
TiXmlElement* elem = new TiXmlElement( "bt" );
elem->SetAttribute("title", "TiTlE");
elem->SetAttribute("save_path", "C:\\Downloads|Test");
elem->SetAttribute("task_finished", "true");
elem->SetAttribute("category", "software");
elem->SetAttribute("size", 7126369);
elem->SetAttribute("hash", "hash_here");
xml.LinkEndChild( elem );
xml.LinkEndChild( parent );
xml.LinkEndChild( root );
xml.SaveFile(pf);
I'm not sure what is happening, but it crashes when it executes that section of code.
I think that your call to access the <MyShares> tag should be
TiXmlElement* parent = root -> FirstChildElement("MyShares");
Also, the only LinkEndChild call you want is the following:
parent->LinkEndChild( elem );
Related
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();
}
}
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.
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?
I am using tinyxml to save data input by the user in a c++ console program. I pass a save function an array of structs that look like the following
struct day
{
string name;
string note;
};
I have seven of these, and pass all seven to the save function that looks like the following
void saveData(day dayArr[])
{
TiXmlDeclaration* declaration = new TiXmlDeclaration("1.0", "UTF-8", "no");//Create DTD
TiXmlDocument* doc = new TiXmlDocument;
doc->LinkEndChild(declaration);
TiXmlElement* week = new TiXmlElement("week");
TiXmlElement* day = new TiXmlElement("day");
TiXmlElement* name = new TiXmlElement("name");
TiXmlElement* note = new TiXmlElement("note");
TiXmlElement* tl = new TiXmlElement("tl");
TiXmlElement* ti = new TiXmlElement("ti");
TiXmlText* dayName = new TiXmlText("");
TiXmlText* dayNote = new TiXmlText("");
for(int i=0; i<7; i++)
{
dayName = new TiXmlText(dayArr[i].name.c_str());
dayNote = new TiXmlText(dayArr[i].note.c_str());
name->LinkEndChild(dayName);
note->LinkEndChild(dayNote);
day->LinkEndChild(name);
day->LinkEndChild(note);
}
week->LinkEndChild(day);
doc->LinkEndChild(week);
doc->SaveFile("test.xml");
cout << "SAVED";
}
It writes this to the file
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<week>
<day>
<name>SundayMondayTuesdayWednesdayThursdayFridaySaturday
</name>
<note>
</note>
</day>
</week>
What i need is this
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<week>
<day>
<name>Sunday</name>
<note> </note>
</day>
<day>
<name>Monday</name>
<note>
</note>
</day>
<day>
<name>Tuesday</name>
<note> </note>
</day>
<day>
<name>Wednesday</name>
<note> </note>
</day>
<day>
<name>Thursday</name>
<note> </note>
</day>
<day>
<name>Friday</name>
<note> </note>
</day>
<day>
<name>Saturday</name>
<note> </note>
</day>
</week>
I can't figure out how to create new elements of the day tag. Thanks in advance for any help.
I haven't used TinyXml before but looking at the structure of the code, you need to create the day element inside your for loop and add it to the week element 7 times - once for each day.
Your current code only adds the day element to the week element once at the end - this is reflected in your xml output.
Taking part of your code - maybe something similar to this below. (This may not compile or be exactly correct but should provide the right idea).
TiXmlElement* week = new TiXmlElement("week");
TiXmlElement* name = new TiXmlElement("name");
TiXmlElement* note = new TiXmlElement("note");
TiXmlElement* tl = new TiXmlElement("tl");
TiXmlElement* ti = new TiXmlElement("ti");
TiXmlText* dayName = new TiXmlText("");
TiXmlText* dayNote = new TiXmlText("");
for(int i=0; i<7; i++)
{
TiXmlElement* day = new TiXmlElement("day");
dayName = new TiXmlText(dayArr[i].name.c_str());
dayNote = new TiXmlText(dayArr[i].note.c_str());
name->LinkEndChild(dayName);
note->LinkEndChild(dayNote);
day->LinkEndChild(name);
day->LinkEndChild(note);
week->LinkEndChild(day);
}
doc->LinkEndChild(week);
void saveData(std::vector<day*> vecDay)
{
TiXmlDeclaration* declaration = new TiXmlDeclaration("1.0", "UTF-8", "no");//Create DTD
TiXmlDocument* doc = new TiXmlDocument;
doc->LinkEndChild(declaration);
TiXmlElement* week = new TiXmlElement("week");
for(std::vector<day*>::iterator it = vecDay.begin(); it != vecDay.end(); it++)
{
TiXmlElement* day_ = new TiXmlElement("day");
TiXmlElement* name = new TiXmlElement("name");
TiXmlElement* note = new TiXmlElement("note");
TiXmlElement* tl = new TiXmlElement("tl");
TiXmlElement* ti = new TiXmlElement("ti");
TiXmlText* dayName = new TiXmlText("");
TiXmlText* dayNote = new TiXmlText("");
dayName = new TiXmlText((*it)->name.c_str());
dayNote = new TiXmlText((*it)->note.c_str());
name->LinkEndChild(dayName);
note->LinkEndChild(dayNote);
day_->LinkEndChild(name);
day_->LinkEndChild(note);
week->LinkEndChild(day_);
}
doc->LinkEndChild(week);
doc->SaveFile("test2.xml");
cout << "SAVED" << endl;
}
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.