Extracting child node values with QXmlItem as a QXmlQuery focus
Asked Answered
M

4

7

I've been trying to fetch node text values from this XML file:

  <!DOCTYPE structure>
  <data>
   <x>
    <id>1</id>
    <nam>tytuł</nam>
    <tab>21</tab>
    <ind>5</ind>
    <pre>TY</pre>
    <khw>C.TY</khw>
   </x>
   <x>
    <id>2</id>
    <nam>autor</nam>
    <tab>21</tab>
    <ind>5</ind>
    <pre>FO</pre>
    <khw>C.FO</khw>
   </x>
   <x>
    <id>3</id>
    <nam>hasło korporatywne</nam>
    <tab>21</tab>
    <ind>5</ind>
    <pre>FN</pre>
    <khw>C.FN</khw>
   </x>
  </data>

What I want to do is to fetch every node and it's children and convert it to QMap. I have no trouble with fetching single element, but when it comes to fetch children items by setting result of QXmlQuery as a focus, the QString that i evaluate the child node query is empty. I use this piece of code:

QXmlResultItems results;
QFile structure("./structure.xml"); // xml file, as described earlier
structure.open(QFile::ReadOnly);

QXmlQuery query;
query.setFocus(&structure);
query.setQuery("data/x");
query.evaluateTo(&results);

QXmlItem next = results.next();
while(!next.isNull()) {
    qDebug() << next.toNodeModelIndex().stringValue(); // everything's fine. It prints contents of <x>'s child nodes
    QXmlQuery childQuery;
    QString r;
    childQuery.setFocus(next);
    childQuery.setQuery("./nam/text()"); // already tested: "/nam/text()", "/nam/string()", "x/nam/string()", "data/x/nam/string()" etc... still no luck.
    childQuery.evaluateTo(&r);
    qDebug() << r; // prints \n but it should print content of <nam> node.

    next = results.next();
}

Software I use: Qt 4.7.2 SDK directly from Qt website, QtCreator 2.3.1 on Windows and Linux (without any difference in this particular case, results are the same). I want to be sure that's the problem of my lack of knowledge, rather than software bug, please help

Matisse answered 17/10, 2011 at 15:38 Comment(2)
The funny thing is that it works with "./id/string()" or even with "./name/string()" if you rename the "nam" tags to "name".Pulmonic
Looks like the same problem described here qt-project.org/forums/viewthread/25725Skater
S
8

Unfortunately, it is unclear from Qt Documentation that in cases when you want to use QXmlQuery::setFocus(const QXmlItem& item) overload in order to query child nodes, you should create corresponding QXmlQuery objects using QXmlQuery(const QXmlNamePool& np) constructor to make them share the same QXmlNamePool object. Such sharing, simply speaking, relates queries to each other.

Considering this, your example should look like following:

QFile structure("./structure.xml");
structure.open(QFile::ReadOnly);

QXmlQuery query;
query.setFocus(&structure);
query.setQuery("data/x");

QXmlResultItems results;
query.evaluateTo(&results);

QXmlQuery childQuery(query.namePool());
while (!results.next().isNull()) {
    childQuery.setFocus(results.current());
    childQuery.setQuery("nam/text()");
    QString r;
    childQuery.evaluateTo(&r);
    qDebug() << r;
}

Moreover, you can go further and reuse initial QXmlQuery object:

QFile structure("./structure.xml");
structure.open(QFile::ReadOnly);

QXmlQuery query;
query.setFocus(&structure);
query.setQuery("data/x");

QXmlResultItems results;
query.evaluateTo(&results);

while (!results.next().isNull()) {
    query.setFocus(results.current());
    query.setQuery("nam/text()");
    QString r;
    query.evaluateTo(&r);
    qDebug() << r;
}
Strachan answered 25/11, 2014 at 2:30 Comment(0)
T
1

Instead of using evaluateTo( QString * ) use the QStringList version. It should work.

Tisatisane answered 4/3, 2012 at 13:19 Comment(1)
Now work. Could you please explain, why it should work?Skater
P
0

It should work like this:

QDomDocument doc("structure");
QFile file("structure.xml");
if( !file.open( IO_ReadOnly ) )
  return -1;
if( !doc.setContent( &file ) )
{
  file.close();
  return -2;
}
file.close();

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

QDomNode n = root.firstChild();
while( !n.isNull() )
{
  QDomElement e = n.toElement();
  if( !e.isNull() )
  {
    if( e.tagName() == "x" )
    {
      QMessageBox::information( 0, "X", e.attribute("id", "")+ "\n" + e.attribute("nam", "" ) + "\n" + e.attribute("tab", ""));
    }
  }

  n = n.nextSibling();
}

The code is doing a message box for every x (don't have qt on this machine, so can't test it right now)

Pilferage answered 17/10, 2011 at 15:57 Comment(1)
I'm afraid that the attribute() method is for fetching attribute for specific element, not for fetching its children. In that particular case I should use firstChild().nodeValue() on variable e. I know that I could use this approach, but what I wanted to avoid is too much code to fetch particular data from XML tree.Rutledge
P
0

I had the same problem, and the solution was to have the query and childQuery be exactly the same. You could rewrite your code as :

while(!next.isNull()) {
    qDebug() << next.toNodeModelIndex().stringValue();
    QString r;
    query.setFocus(next);
    query.setQuery("./nam/text()");
    query.evaluateTo(&r);
    qDebug() << r;

    next = results.next();
}

if childQuery is to be in another procedure, you have to pass it by reference.

Pasco answered 28/2, 2014 at 14:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.