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 writeconst 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 aQDomNodeList
...How would you guys do this elegantly?
-
Hi,
Based on the XML you gave
elementsByTagName
looks like the right choice, you'll have a list of one element forthree
. -
@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 multiplefirstChildElement
s.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 ofQXmlDocument
(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.