15.4.  XML Streams

[ fromfile: xmlstreams.xml id: xmlstreams ]

QXmlStreamReader and QXmlStreamWriter provide a faster and more powerful API for reading and writing XML. They are not part of Qt's XML module either – they have been in QtCore since Qt 4.3.

[Important] Why Not Use the "Standard" APIs?

DOM, although convenient and standard, has high memory and processing requirements and could be much simpler. SAX, although fast and relatively memory efficient, could be faster but for the need to create certain standard structures during parsing, and is not very simple because it requires an understanding of inheritance and pure virtual functions to use properly.

The interfaces in Qt's XML module are based on interfaces that are part of the Java standard, and are also available in other languages, so they are easy to pick up if you've used them before. But if you already know them, it should be easy to pick up another XML parsing API such as this one.

There is no tree structure generated by the QXmlStreamReader API. Example 15.17 shows a class that uses it to build one. It is itself a QStandardItemModel and creates instances of QStandardItem for each tree node.

Example 15.17. src/xml/streambuilder/xmltreemodel.h

[ . . . . ]
class XmlTreeModel : public QStandardItemModel {
    Q_OBJECT
public:
    enum Roles {LineStartRole = Qt::UserRole + 1,
                LineEndRole};    1
    explicit XmlTreeModel(QObject *parent = 0);
public slots:
    void open(QString fileName);
private:
    QXmlStreamReader m_streamReader;
    QStandardItem* m_currentItem;
};
[ . . . . ]

1

Custom roles for data()


The code that does the actual parsing is shown in Example 15.18.

Example 15.18. src/xml/streambuilder/xmltreemodel.cpp

[ . . . . ]
void XmlTreeModel::open(QString fileName) {
    QFile file (fileName);                                1    
    if (!file.open(QIODevice::ReadOnly)) {
        qDebug() << "Can't open file: " << fileName;
        abort();
    }
    m_streamReader.setDevice(&file);                      2
    while (!m_streamReader.atEnd()) {
        QXmlStreamReader::TokenType tt = m_streamReader.readNext();
        switch (tt) {
            case QXmlStreamReader::StartElement: {
                QString name = m_streamReader.name().toString();
                QStandardItem* item = new QStandardItem(name);
                item->setData(m_streamReader.lineNumber(), 
                              LineStartRole);             3
                QXmlStreamAttributes attrs = m_streamReader.attributes();
                QStringList sl;
                sl << tr("Line# %1").arg(m_streamReader.lineNumber());
                foreach (QXmlStreamAttribute attr, attrs) {
                    QString line = QString("%1='%2'").arg(attr.name().toString())
                                    .arg(attr.value().toString());
                    sl.append(line);
                }
                item->setToolTip(sl.join("\n"));
                if (m_currentItem == 0) 
                   setItem(0, 0, item);                   4
                else 
                   m_currentItem->appendRow(item);        5
                m_currentItem = item;
                break; }
            case QXmlStreamReader::Characters: {
                QString tt = m_currentItem->toolTip();
                tt.append("\n");
                tt.append(m_streamReader.text().toString());
                m_currentItem->setToolTip(tt);
                break; }
            case QXmlStreamReader::EndElement: 
                m_currentItem->setData(m_streamReader.lineNumber(), LineEndRole);
                m_currentItem = m_currentItem->parent();  6
                break;
            case QXmlStreamReader::EndDocument:
            default:
                break;
        }
    }
}

1

Closes automatically when out of scope.

2

Start parsing - can use any QIODevice.

3

Custom data()

4

Set root item in model.

5

Add as a child to the current item.

6

Go up the tree.


Figure 15.7.  TreeBuilder Example

TreeBuilder Example