Using QDomNode::firstChildElement() repeatedly feels weird...



  • For an XML document structure like

    <one>
        <two>
            <three />
        </two>
    </one>
    

    when I need to get the <three /> element, I currently write

    const auto three = doc
        .firstChildElement("one").firstChildElement("two").firstChildElement("three");
    

    but this feels weird. Often, I have wondered if there isn't a shorter way to do this with a single command, something like

    const auto three = doc.someUnknownFunction("three");
    

    I don't think elementsByTagName() is what I want, as that returns a QDomNodeList...

    How would you guys do this elegantly?


  • Lifetime Qt Champion

    Hi,

    Based on the XML you gave elementsByTagName looks like the right choice, you'll have a list of one element for three.



  • elementsByTagname is the shortest/fastest code if you don't care about where the <three />s occur in the structure. If you want to ensure it's <one><two><three> you do have to do it via multiple firstChildElements.


  • Qt Champions 2016

    @Bart_Vandewoestyne said in Using QDomNode::firstChildElement() repeatedly feels weird...:

    How would you guys do this elegantly?

    By not doing it. ;)
    What I'd do is to create my document in C++ in an object-oriented fashion - each element is represented by a specific class all of which derive from a specific interface. For example I'd have something along:

    class MyCoolFormatElement
    {
    public:
        virtual bool load(const QDomElement &) = 0;
    };
    
    class MySecondElement : public MyCoolFormatElement
    {
    public:
         bool load(const QDomElement & element) override
         {
              // Do something for this element to extract the data, e.g.
              textData = element.text();
         }
    private:
         QString textData; //< Or whatever it contains
    };
    
    class MyFirstElement : public MyCoolFormatElement
    {
    public:
        bool load(const QDomElement & element) override
         {
              // Do something for this element to extract it's own private data (for example values from attributes)
              // ...
              // Pass on to the child element we expect (there's none, then we have a default value set in it's constructor)
              QDomNodeList tagList = element.elementsByTagName("two");
              if (tagList.count() == 0)
                  return false;
    
              secondElement.load(tagList.at(0).toElement());
         }
    private:
         MySecondElement secondElement;
    };
    

    And you'd load the data from the root element of the document:

    QDomDocument document;
    MyFirstElement myCoolDocumentFormat;
    
    if (!myCoolDocumentFormat.load(document.documentElement()))
        ; // Oops
    

    Obviously there's a lot of possible variability, but in my mind it definitely beats the naive approach.

    PS. To fully fluff it up, one should in principle keep one string for the tag name (initialized in the constructor) in the element class itself and use that instead of hardcoding the string in the parent's load. E.g:

    QDomNodeList tagList = element.elementsByTagName(secondElement.name());
    


  • @JNBarchan said in Using QDomNode::firstChildElement() repeatedly feels weird...:

    elementsByTagname is the shortest/fastest code if you don't care about where the <three />s occur in the structure. If you want to ensure it's <one><two><three> you do have to do it via multiple firstChildElements.

    OK, thanks. That confirms what I was thinking :-)



  • @Bart_Vandewoestyne said in Using QDomNode::firstChildElement() repeatedly feels weird...:

    How would you guys do this elegantly?

    I am a beginner at Qt, so I don't know what the relationship is between how you get a QDomDocument versus some kind of QXmlDocument (which in itself does not seem to exist).

    However, in case you were not aware, the normal/simplest/shortest way of doing this kind of query is via XPath/XQuery, and I see Qt has QXmlQuery for this (http://doc.qt.io/qt-5/qxmlquery.html#details).

    Then your search for the node would be like:

    QXmlQuery query;
    query.setQuery("/one/two/three");
    

    The point being, XPath/XQuery (http://doc.qt.io/qt-5/xquery-introduction.html) lets you specify your queries in a string and it does the tree descent for you, there's no code for you to write. It has advanced features for all sorts of queries, e.g. including the ability to search by attributes as well as node names/types, which can be lot simpler/clearer than writing explicit code.

    As an extra "goodie", QXmlQuery gives you access to XSL/XSLT stylesheets, which can be really useful/simple for transforming XML for output.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.