Development/Tutorials/QtDOM Tutorial

< Development‎ | Tutorials
Revision as of 15:50, 20 January 2007 by Reinhold (Talk | contribs) (Link to Qt Docu)

Jump to: navigation, search

Short introduction to XML





 <name>Holidays for Austria</name>
   <name>New Year's Day</name>


Such an XML file can be represented as a tree structure, with the XML document being the root of the tree, and each subelement/attribute/text value is a child of it's enclosing XML element. The tree structure corresponding to the holiday file above looks like the following:

QtDOM TreeStructure.png

As you can see, there are different types of nodes:

  • elements: they are of the form <tagname>...</tagname>
  • attributes: they are attributes inside element tags: <tagname attribute=value>
  • text nodes: the text content of element tags, the text between <tagname> and </tagname>
  • processing instructions: they tell the xml parser / transformer / viewer how to interpret something, their form is <? instruction attribute=value ?>
  • comments: <!-- comment -->
  • document type: specifies the type of the document (e.g. html 4 transitional), its form is <!DOCTYPE name >

Notice that we did use the same tag <name> inside the <holidayset> and inside the <holiday> tags. We used quite generic names for the tags, which might become a problem with complexer structure, when we want to use the same name for different purposes. For this reason, XML also defines namespaces to allow for the same name (but from a different namespace, so they are actually different names) used in different context. In a later section we will look at these namespaces.

Also note that we implicitly used specially formated (ISO-formatted) contents for the date tags, without yet specifying it. Of course we could give any other value, say <date>I hope never</date>, and it would still be a valid XML file, but the parser will not be able to interpret the value as a date. Add such constraints and specific formats / values / value ranges for elements is possible using either a [[wikipedia>DTD]] or an XML Schema. If you have such a definition, a validating parser can check whether a given XML file really adheres to the document structure defined in that schema. Unfortunately, the Qt XML/DOM classes are not validating parsers, so you cannot validate XML documents against a given schema with Qt.

We will use the example from above throughout this tutorial. In our application, we want to store the holiday set in the following class:

class Holiday { public:

 Holiday() {}
 ~Holiday() {}
 QDate mDate;
 QString mName;


class HolidaySet { public:

 ~HolidaySet() {}
 QString mCountry, mName;
 QList<Holiday> mHolidays;




Creating a simple XML file with Qt DOM




  1. Create the DOM document
  2. For each element of the dom tree:
    2. Insert the element into its parent node.
    3. If the element should have contents, set the contents, set the attributes, etc.




QDomElement addElement( QDomDocument &doc, QDomNode &node,

                       const QString &tag, 
                       const QString &value = QString::null )


 QDomElement el = doc.createElement( tag );
 node.appendChild( el );
 if ( !value.isNull() ) {
   QDomText txt = doc.createTextNode( value );
   el.appendChild( txt );
 return el;


QString holidaySetToXML( const HolidaySet &hs ) {

 QDomDocument doc;
 QDomProcessingInstruction instr = doc.createProcessingInstruction( 
                   "xml", "version='1.0' encoding='UTF-8'");
 // generate holidayset tag as root, add country attribute if needed
 if ( !hs.mCountry.isEmpty() ) 
   holidaySetElement.setAttribute( "country", hs.mCountry );
 if ( !hs.mName.isEmpty() ) 
 QList<Holiday>::iterator i;
 return doc.toString();



Let us now slowly step through the code:

    • First it creates an empty DOM document (line 19)
    • The processing instruction <?xml ...?> is created in line 20 and inserted as the first child of the document in line 21.
    • The root element (<holidayset>...</holidayset>) is then created using our addElement helper function (line 25). Here the DOM document and the parent are the same (doc). The holidaySetElement is still empty, i.e. we now have an XML repesentation
    • To set an attribute (country="at") for the DOM node, we call QDomElement::setAttribute( key, value ) (line 27). Our document now has the XML representation
      <holidayset country="at"/>
    • To populate the holidayset with the <holiday>...</holiday> entries, we create a new element (line 36) for each of the holidays in a loop. The parent of all these elements is the holiday set element. After line 36 we have an XML

You can now create the XML file contents simply via





Loading a simple XML file using Qt DOM




In this chapter we will now look at how we can parse the XML holiday set into the HolidaySet structure using the DOM method. The parsing using a SAX parser will be treated in a later chapter.

The usual way to parse an XML document into a DOM tree is to use the method QDomDocument::setContent. If this method is successful, the QDomDocument object contains the DOM tree in the usual DOM structure. If an error occurs, the error message and the exact position of the error is stored into the parameters of the call:

QFile f( argv[1] ); QDomDocument doc; QString errorMsg; int errorLine, errorColumn; if ( doc.setContent( &f, &errorMsg, &errorLine, &errorColumn ) ) {

 result = parseXMLwithDOM( doc );


The resulting QDomDocument now contains the whole DOM tree, similar to the one that we created in the previous section. The DOM tree represents the hierarchical structure as shown in the image above. To obtain an element with a given tag name, one can use the QDomNode::namedItem( "tagname" ) method of the parent node object, which returns a QDomNode (this class is the base type to describe any of the DOM nodes). As we are interested in the element, we have to convert it to a DOM element by the toElement() method. You might find this to be quite awkward to use, in particular since C++ usually offers better ways to work with derived classes. However, the DOM API was designed to work with various different languages, so typical C++-isms cannot be part of the DOM API.

To extract the holiday data from the DOM tree, we write the function parseXMLwithDOM: HolidaySet parseXMLwithDOM( QDomDocument &domTree ) {

 HolidaySet hs( QString::null );

We first obtain the DOM element that represents the <holidayset>...</holidayset> element using the namedItem and toElement methods. If no child node named "holidayset" exists or it is not an element (e.g. because it is a processing instruction or an attribute), a null element is returned:

 QDomElement set = domTree.namedItem("holidayset").toElement();
 if ( set.isNull() ) {
   qWarning() << "No <holidayset> element found at the top-level "
              << "of the XML file!";
   return hs; // no holiday set found

This element possibly has an attribute named "country". This can be checked using the QDomNode::hasAttribute method and the attribute can be obtained using the QDomNode::attribute method:

if ( set.hasAttribute("country") ) {

 hs.mCountry = set.attribute("country");


We can also have a <name>...</name> child element, which can be obtained similar to the holidayset element. We retrieve the text of the element between the enclosing tags by a call to QDomElement::text(): // Search for a given element (only the first matching is returned): QDomElement name = set.namedItem("name").toElement(); if ( !name.isNull() ) { // We have a <name>..</name> element in the set

 hs.mName = name.text();


The namedItem method will always return the first child that matches the given name. While this might be fine for the <name> child element, we cannot use this for the <holiday>...</holiday> elements, which will appear more than once. For this reason, there are iterator-like calls firstChild() and and nextSibling() to walk through all child nodes and firstChildElement("tagname") and nextSiblingElement("tagname") to walk only through all child elements with the given tag name. If no further child can be found, a null node is returned:

// Way 1: Loop through all child nodes with a given tag name. QDomElement e = set.firstChildElement( "holiday" ); for ( ; !e.isNull(); e = e.nextSiblingElement( "holiday" ) ) {

 Holiday h;
 // e is the <holiday>...</holiday> element....
 // Load the contents of e into h
 hs.mHolidays.append( h );


// Way 2: Loop through all child nodes and check if it is an element // with one of the wanted tagnames QDomNode nd = set.firstChild(); for ( ; !nd.isNull(); nd = nd.nextSibling() ) {

 if ( nd.isElement() && nd.toElement().tagName() == "holiday" ) {
   QDomElement e = nd.toElement();
   Holiday h;
   // Same code as above...
   // Load the contents of e into h
   hs.mHolidays.append( h );


The first method is of course the better and simpler choice in our example. However, if the <holidayset> element can have various different child elements, it's often easier and faster to loop through all children only once and condition the code on the name of the tag, which is done in the second example.

If one looks at the API documentation of the QDomElement class, one will also find a method elementsByTagName, which returns a QDomNodeList that can be traversed using iterators. The problem with this method is that it is recursive, i.e. it will return all child elements with the given name at any level (in a well-defined canonical order). We, however, only want the immediate children of the <holidayset> element, but not sub- or subsubchildren.

Now that we have the QDomElement of the <holiday>...</holiday> element, we can easily load its contents into the Holiday structure, using only methods that we have already seen:

Holiday h; QDomElement v = e.namedItem("name").toElement(); if ( !v.isNull() ) h.mName = v.text(); v = e.namedItem("date").toElement(); if ( !v.isNull() ) {

 h.mDate = QDate::fromString( v.text(), Qt::ISODate );

} hs.mHolidays.append( h );

This concludes our method for loading the holiday set data from the DOM tree representation into the HolidaySet data structure. The whole parseXMLwithDOM function thus reads:

HolidaySet parseXMLwithDOM( QDomDocument &domTree ) {

 HolidaySet hs( QString::null );
 if ( set.isNull() ) {
              << "of the XML file!";
   return hs; // no holiday set found
 if ( set.hasAttribute("country") ) {
   hs.mCountry = set.attribute("country");
 // Way 1: Explicitly search for a given element:
   hs.mName = name.text();
 // Way 2: Loop through all child nodes with a given tag name.
 QDomElement n = set.firstChildElement( "holiday" );
 for ( ; !n.isNull(); n = n.nextSiblingElement( "holiday" ) ) {
   Holiday h;
   QDomElement e = n.toElement();
   if ( !v.isNull() ) {
   hs.mHolidays.append( h );
 // Way 3: Loop through all child nodes and check if it is an element 
 //        with one of the wanted tagnames
 QDomNode nd = set.firstChild();
     QDomElement n = nd.toElement();
     // Same code as above...
 return hs;


Introduction to XML Namespaces

See also the Qt documentation on namespaces (available at the QtXML page) for a nice example of an xml file where namespaces are useful.


   <h:name>New Year's Day</h:name>


Generating XML documents with namespaces using Qt

Loading XML documents with namespaces using Qt


   KoXmlNode n = node.firstChild();
           return n.toElement();
   return KoXmlElement();


From KODom.h (by dfaure):


* This namespace contains a few convenience functions to simplify code using QDom
* (when loading OASIS documents, in particular).
* To find the child element with a given name, use KoDom::namedItemNS.
* To find all child elements with a given name, use
* QDomElement e;
* forEachElement( e, parent )
* {
*     if ( e.localName() == "..." && e.namespaceURI() == KoXmlNS::... )
*     {
*         ...
*     }
* }
* Note that this means you don't ever need to use QDomNode nor toElement anymore!
* Also note that localName is the part without the prefix, this is the whole point
* of namespace-aware methods.
* To find the attribute with a given name, use QDomElement::attributeNS.
* Do not use getElementsByTagNameNS, it's recursive (which is never needed in KOffice).
* Do not use tagName() or nodeName() or prefix(), since the prefix isn't fixed.
* @author David Faure <>

Loading XML documents using Qt and the SAX parser

class HolidayHandler : public QXmlDefaultHandler { public:

 HolidayHandler() : QXmlDefaultHandler(), holiday(0), holidayset(QString::null) {}
 bool startElement( const QString &/*namespaceURI*/, const QString &localName, const QString &/*qName*/, const QXmlAttributes & atts )
   if ( localName == "holidayset" ) {
     QString country = atts.value("value");
     if ( !country.isEmpty() ) holidayset.mCountry = country;
   } else if ( localName == "holiday" ) {
     if ( !holiday ) holiday = new Holiday;
   return true;
 virtual bool endElement ( const QString &/*namespaceURI*/, const QString & localName, const QString &/*qName*/ )
   if ( localName == "name" ) {
     if ( holiday ) holiday->mName = content;
     else holidayset.mName = content;
   } else if ( localName == "date" ) {
     QDate d = QDate::fromString( content, Qt::ISODate );
     if ( d.isValid() && holiday ) holiday->mDate = d;
   } else if ( localName == "holiday" ) {
     holidayset.mHolidays.append( *holiday );
     holiday = 0;
   return true;
 bool characters(const QString &str)
   content += str;
   return true;
 bool fatalError(const QXmlParseException &exception)
   qDebug() << "Parse error at line " << exception.lineNumber() 
            << ", column " << exception.columnNumber() << ":\n" 
            << exception.message();
   return false;
 HolidaySet holidaySet() { return holidayset; }


 Holiday *holiday;
 HolidaySet holidayset;
 QString content;



 if ( argc < 2 ) {
   qWarning() << "Please give the XML file as argument!"<<endl << "Usage: qtdom_parse_dom filename.xml"<<endl;
   return EXIT_FAILURE;
 HolidaySet result( QString::null );
 HolidayHandler handler;
 QXmlSimpleReader reader;
 QFile file(argv[1]);
 if (! | QFile::Text)) {
     QMessageBox::warning(0, "QtDOM SAX example",
                          QString("Cannot read file %1:\n%2.")
     return EXIT_FAILURE;
 QXmlInputSource xmlInputSource(&file);
 if ( reader.parse(xmlInputSource) ) {
   result = handler.holidaySet();
 } else {
   return EXIT_FAILURE;

Initial Author: Reinhold Kainhofer