PDA

View Full Version : QDomNode::replaceChild Question



KineticArc
21st September 2010, 18:27
I've been messing with XML configuration data for a simulation application I am building, and have run into some confusing problems. Firstly, I'm walking an XML tree using DOM, with Qt 4.6.2.

The XML file looks like this (settings.xml):


<?xml version='1.0' encoding="UTF-8"?>
<settings>
<configuration name="Eddie">
<execdir>Execution path of Eddie</execdir>
<path>Path to Eddie GUI</path>
<binary>Eddie GUI binary</binary>
</configuration>
</settings>


Pretty straightforward, eh?

The problem that I'm running into is that the replaceChild will return a null QDomNode. It should point to the new old node. According to the documentation, replaceChild failed, since it is returning with a null node. I can get this code to compile and run two different ways, but the one way doesn't work.

For further clarification (from the main.cpp file):



QDomDocument doc("stuffage");
QFile file("../src/settings.xml");
doc.setContent(&file);

QDomElement root = doc.documentElement();
if(root.tagName() != "settings")
return -3;

QDomNode n = root.firstChild();
while(!n.isNull())
{
QDomElement e = n.toElement();
if(!e.isNull())
{
if((e.tagName() == "configuration") and (e.attribute("name", "") == "Eddie"))
{
QDomElement e2 = n.firstChildElement();
if(!e2.isNull())
{
while(!e2.isNull())
{
if(e2.tagName() == "execdir")
{
// The following works... we'll call this Exhibit 1
QDomNode currNode = e2.firstChild();
currNode.setNodeValue("NEW DATA HERE");

// As does this portion of code... we'll call this Exhibit 2
QDomText text = doc.createTextNode("NEW DATA HERE");
e2.replaceChild(text, e2.firstChild());

// This DOESN'T work! It will replace this and only this instance.
// replaceChild is returning with a null QDomNode, and e2 thus
// will return true when isNull is invoked. Almost as if the nodes
// in the list aren't hooked up correctly...
//
// We'll call this Exhibit 3
QDomElement elem = doc.createElement("execdir");
elem.appendChild(doc.createTextNode("NEW DATA HERE");
n.replaceChild(elem, e2);
}
else if(e2.tagName() == "binary")
{
// This portion looks exactly the same as above, only with different
// data being pushed.
}

e2 = e2.nextSiblingElement();
}
}
}
}

n = n.nextSibling();
}

// Perform writes to the settings.xml file
file.remove();
file.open(QIODevice::ReadWrite);
QTextStream ts(&file);
ts<<doc.toString(4);
file.close();



I'm just trying to figure out how I can replace an existing tag with data in it on the fly. I want to be able to look at an element, determine if it is needing changed, and if it does, change it, and move to the next element to perform the same logic (check if it needs changed, change it, then move on again).

As stated above, Exhibit 1 and 2 work, but not 3. It's like I'm deleting something from an STL list, but didn't reset the iterator to the new position.

So for further clarification, I do not have Exhibit 1, 2, and 3 all together. Only one or the other is being used. I am looking at two tags that are going to need to be updated whenever configuration settings change on a child form. Therefore, I need to incrementally make my changes, then shove them off to a .xml flat-file. I can do this by performing a setNodeValue on the QDomText of each of the execdir, path, and binary tags. I can also create a text node, and replace the text node currently contained within the execdir, path, and binary tags. I can do this with all the elements that are in the tree. In other words, if I only had one tag, it would work for that one tag. If I had fifty, then it would work for all fifty. However, if I attempt Exhibit 3, it would change only the first match, then exit out of the loop, since e2 is null.

Any suggestions?

Dan Milburn
21st September 2010, 23:54
The code you have provided does not check the return value of n.replaceChild() at all. It would be more helpful if you could provide the actual code you are using. In any case, you are replacing e2, that is once you've called n.replaceChild(elem, e2); the node referenced by e2 is no longer a child of node n and so continuing to iterate over it will fail. What you probably want is to put e2 = elem; after the replace call.

KineticArc
22nd September 2010, 20:05
Wow... that was a stupid mistake. It essentially is like a link in a link list. Once you append the element into the list of XML tags, that element becomes the new link in the chain, as the previous element was replaced by the new element. Therefore, the links to the previous and next sibling elements are null pointers. Therefore, by doing the following, I got it to work with your recommendation. (oh, I did have it returning e2 = n.replaceChild(elem, e2), but that was incorrect, as it needs to be the next sibling element. I just didn't have it reflected in the code I posted above, as it was just a way to quickly convey what I was trying to express).



QDomNode n = root.firstChild();
while(!n.isNull())
{
QDomElement e = n.toElement();
if(!e.isNull())
{
if((e.tagName() == "config") and (e.attribute("name", "") == "Eddie"))
{
QDomElement e2 = n.firstChildElement();
while(!e2.isNull())
{
if(e2.tagName() == "execdir")
{
QDomElement elem = doc.createElement("execdir");
elem.appendChild(doc.createTextNode("EXECUTABLE DIRECTORY"));
e2 = n.replaceChild(elem, e2).nextSiblingElement();
}
else if(e2.tagName() == "binary")
{
QDomElement elem = doc.createElement("binary");
elem.appendChild(doc.createTextNode("EXECUTABLE"));
e2 = n.replaceChild(elem, e2).nextSiblingElement();
}
else e2 = e2.nextSiblingElement();
}
}
}

n = n.nextSibling();
}



That now works, as stated. Just thought I would post the final code for anyone else looking at the same dumb mistake I was making.