1

I'm new to Qt and QML. With that said, I am making a editable list in QML which I would like to import and export as XML file. Right now I'm stuck on importing it from XML file and setting it as my ListView's model from the C++ side.

I'm hoping I can transform the XML to a form I want to be able to add, remove and edit rows in my ListView from the QML side, so using XmlListModel looks like a bad idea, i.e. it doesn't offer those abilities.

my main.qml in which I am choosing the file location:

    FileDialog {
        id: exportDialog
        title: "Please choose an XML TV file"
        nameFilters: [("*.xml")]

        onAccepted: {
            fileio.parse(importDialog.fileUrl)
        }
        onRejected: {
            console.log("Canceled")
        }
    }

My C++ header file in which I would like the conversion to happen:

#ifndef FILEIO_H
#define FILEIO_H

#include <QObject>
#include <QFile>
#include <QTextStream>
#include <QXmlStreamReader>
#include <QDebug>

class FileIO : public QObject
{
    Q_OBJECT

public slots:
    bool parse(const QString& source)
    {
        if (source.isEmpty())
            return false;

        QFile file(source);
        if (!file.open(QFile::WriteOnly | QFile::Truncate))
            return false;    

        QFile* mjau = new QFile(source);
        if (!mjau->open(QIODevice::ReadOnly | QIODevice::Text)) {
                printf("Load XML File Problem");
                return false;
        }

        QXmlStreamReader reader(mjau);
        while(!reader.atEnd() && !reader.hasError()) {
            if(reader.readNext() == QXmlStreamReader::StartElement && reader.name() == "parent") {
                 qDebug() << reader.readElementText();
                //printf(reader.readElementText())
            }
        }
    }
public:
    FileIO() {}
};

#endif // FILEIO_H

Example of xml file I'd like to parse, multitude of entries:

<notes>
    <note>
        <heading>Help</heading>
        <body>I want to make this work!</body>
    </note>
    <note>
        <heading>Because</heading>
        <body>I love QML <3</body>
    </note>
    ...
</notes>

To something like this:

ListModel {
    id: notes
    ListElement {
        heading: "Help"
        body: "I want to make this work!"
    }
    ListElement {
        heading: "Because"
        body: "I love QML <3"
    }
}

Right now, when I choose the XML file nothing happens, but that same file is "cleaned out", becomes a blank .xml file. How could I parse it properly, and after that, convert the parsed data into QML's ListView ListModel or any model that can support editing from the QML's side?

--UPDATE-- Parsing of the XML file will be a one-time procedure, after the parse I intend to work with the data in javascript, so it would be cool if the data could be parsed in JSON object structure.

Romy
  • 407
  • 6
  • 25

2 Answers2

2

You should not convert it into a ListModel. Instead implement your own descendent of the QAbstractListModel that has your needed capatibilities.

However there is likely a reason, why Qt does provide the XmlListModel as read-only. You can't just insert into a file. (AFAIK) So if a entry is added or removed, and that change is not at the very end of the file, the whole content needs to be copied. And that is needed to be done, when ever the data changes. Not good for performance.

If you consider doing something like that, don't write back permanently. Think, when it is a good time to do the write-back. An obvious point is when the model is being destroyed. But as there might be some problems, so the object can't be destroyed properly, you might want to save every now and then.

You might use a timer, to save, e.g. 10 Seconds after a change (expecting, that if one change happened, many changes will happen soon after)

You might also use a more complex format for the XML, where you indeed just append basically logging the changes. From time to time you do a clean-up rewriting the file, so that the history is lost, but the changes are really applied to your xml structure.

  • All valid points. However, I'm planning to work with very small xml files, 100-200kb in size. But even that isn't the problem, I'm very much open to any method that can just parse the xml file in a more editable form, be it sqlite database like @eyllanesc commented, or a simple JSON object model. After I get the data out of XML, its easy to manipulate it later. The parsing is what is bothering me right now. – Romy Nov 02 '17 at 09:15
0

In the end, I've usedQDomDocument class to read XML file, iterate through all of the nodes and write values that I want to JSON objects like so:

QDomElement root = doc.firstChildElement();
QDomNodeList programme = root.elementsByTagName("node");
nodeCollection.push_back(ListElements(program, "title", "lang"));

...

QJsonObject ListElements (QDomElement root, QString tagname, QString attribute)
{

        QDomNodeList items = root.elementsByTagName(tagname);

        for(int i = 0; i < items.count(); i++)
        {
            QDomNode itemnode = items.at(i);
            if (itemnode.isElement())
            {
                QDomElement itemElement = itemnode.toElement();
                qInfo() << itemElement.tagName(); // <-element name
                qInfo() << itemElement.attribute("lang"); // <-element attribute
                qInfo() << itemElement.text(); // <-element text

// putting the values above inside JSOn object:

                auto nodeData = QJsonObject(
                {
                qMakePair(QString(itemElement.tagName()), QJsonValue(itemElement.text())),
                qMakePair(QString("lang"), QJsonValue(itemElement.attribute("lang"))),
                });

                return nodeData;
            }
        }
    }

so this:

  <node>
    <title lang="en">Title1</title>
    <category lang="en">Category1</category>
    <desc lang="en">Description1</desc>
  </node>

becomes (this is what ListElements returns):

 QJsonObject({"title":"Title1","lang":"en"})
 QJsonObject({"category":"Category1","lang":"en"})
 QJsonObject({"desc":"Description1","lang":"en"})
Romy
  • 407
  • 6
  • 25