handle
handle

Reputation: 6319

Qt C++ QDomDocument, Recursively iterating over XML data, retrieving text elements without child data

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

Answers (2)

Troyseph
Troyseph

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

handle
handle

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

Related Questions