Reputation: 6319
I have an XML document like this
<root>
<stuff>
<data foo="bar">
<name>barf</name>
<value>123</value>
</data>
<!-- ... --->
</stuff>
</root>
And would like to recurse through the tree to get all information from it, like
data bar
name barf
value 123
However, DomElement::text() also returns the children's text contents, i.e. "barf 123" for the data
element
I found some fairly compact code to recurse through a DOM:
void MyClass::TraverseXmlNode(const QDomNode& node)
{
QDomNode domNode = node.firstChild();
QDomElement domElement;
while(!(domNode.isNull()))
{
if(domNode.isElement())
{
domElement = domNode.toElement();
if(!(domElement.isNull()))
{
qDebug() << __FUNCTION__ << "" << domElement.tagName() << domElement.text();
}
}
TraverseXmlNode(domNode);
domNode = domNode.nextSibling();
}
}
Upvotes: 1
Views: 5736
Reputation: 5138
For those of you landing here looking for compact code to iterate a QDomDocument
:
void recurseQDomNode(QDomNode node, int depth, const std::function<void (QDomNode&, int depth)>& perNodeAction)
{
QDomNodeList paths = node.childNodes();
perNodeAction(node, depth);
for (int i = 0; i < paths.count(); ++i) {
recurseQDomNode(paths.at(i), depth + 1, perNodeAction);
}
}
And an example solving the question
int main(int argc, char *argv[])
{
QDomDocument doc;
doc.setContent(QString(R"(<root>
<stuff>
<data foo="bar">
<name>barf</name>
<value>123</value>
</data>
<!-- ... --->
</stuff>
</root>
)"));
recurseQDomNode(doc.documentElement().firstChild(), 0, [](QDomNode& node, int depth)
{
if (node.isElement()) {
QString output(depth, ' ');
auto element = node.toElement();
output += element.tagName();
// Add attribute values to the line
auto attributeMap = element.attributes();
for(int i = 0; i < attributeMap.count(); ++i) {
auto attribute = attributeMap.item(i).toAttr();
output += " " + attribute.value();
}
// Add element string content to the line
if (element.firstChild().isText()) {
output += " " + element.firstChild().toText().data();
}
qDebug() << output;
}
});
return 0;
}
Outputs:
"stuff"
" data bar"
" name barf"
" value 123"
Upvotes: 0
Reputation: 6319
The solution is very simple: do not output an element's text. You want to look for text nodes instead:
void MyClass::TraverseXmlNode(const QDomNode& node)
{
QDomNode domNode = node.firstChild();
QDomElement domElement;
QDomText domText;
static int level = 0;
level++;
while(!(domNode.isNull()))
{
if(domNode.isElement())
{
domElement = domNode.toElement();
if(!(domElement.isNull()))
{
qDebug() << __FUNCTION__ << "isElement" << level << QString(level, ' ').toLocal8Bit().constData() << domElement.tagName().toLocal8Bit().constData();
QDomNamedNodeMap nma = domElement.attributes();
int l = nma.length();
for( int i=0; i < l; i++ )
{
QDomAttr tempa = nma.item(i).toAttr();
qDebug() << __FUNCTION__ << "isElement" << level << QString(level, ' ').toLocal8Bit().constData() << "attribute" << i << tempa.name().toLocal8Bit().constData() << tempa.value().toLocal8Bit().constData();
}
}
}
if(domNode.isText())
{
domText = domNode.toText();
if(!domText.isNull())
{
qDebug() << __FUNCTION__ << "isText " << level << QString(level, ' ').toLocal8Bit().constData() << domText.data().toLocal8Bit().constData();
}
}
TraverseXmlNode(domNode);
domNode = domNode.nextSibling();
}
level--;
}
Upvotes: 3