Qt & JSON

published at 30.04.2014 20:09 by Jens Weller

With Qt5 there is a new API for reading and writing JSON files in Qt. In the last days I had the chance to play around with this API, as I implemented importing and exporting different data sets from and to JSON. Qt has a very good starting page for JSON in its documentation, and has already also an example that shows the saving and loading of data to a JSON File, which I do recommend for further reading.

C++ & JSON

There are a number of JSON libraries, a useful overview on which are your options gives this JSON benchmark code on github. It also can give you some numbers on performance for your use cases. I decided to use the API from Qt, as I needed only a simple solution to read and write JSON, and this is with Qt5 already on board. This is what also makes Qt JSON implementation rather unique: You most likely will not consider using it, when you don't already have Qt5 on board.

JSON is a rather small data format, not only from its footprint but also from what data types it can handle:

So far I had no use case for bool and Null. Qt implements this as the QJsonValue class, which is a variant-like type storing its value into a union. You can query which value it is currently holding by calling isBool, isDouble etc. The conversion to the type is then done with the function toBool, toDouble, also a toVariant is offered, which is the only support for a non-JSON type. The type of a QJsonValue object can also be queried via the type() function. JSON Arrays and Objects are represented by the QJsonArray and QJsonObject class.

Reading JSON with QJsonDocument

To start working with JSON in Qt one first needs a valid instance of QJsonDocument, which for reading is simply obtained through the static method QJsonDocument::fromJson:

    QJsonParseError jerror;
    QJsonDocument jdoc= QJsonDocument::fromJson(file.readAll(),&jerror);
    if(jerror.error() != QJsonParserError::NoError)
        return false;
    QJsonObject obj = jdoc.object();

The method fromJson reads from a QByteArray into the document instance. You can hand over an optional pointer to a QJsonParserError instance, which lets you know after the parsing if the document could be read successfully. With object() you get access to the root object of the JSON document. Reading the talk data in my voting tool was then quite easy:

    voteid = obj["voteid"].toString();
    if(voteid.isEmpty())
        return false;
    QJsonArray talks = obj["talks"].toArray();
    QString id,title,desc,comment;
    int vote=1;
    for(auto&& item: talks)
    {
        const QJsonObject& talk = item.toObject();
        id = QString::number(talk["id"].toDouble());
        vote = static_cast(talk["vote"].toDouble());
... pushTalk(id,title,desc,comment,vote); }

Reading single values from the JSON Object can be easily done via the []-operator. The talks are stored inside of an QJsonArray, which is easily accessible via a ranged-for. As each value in the array is an object, the first step is to obtain this root object for each talk. After its fully extracted it gets pushed into the container. Sometimes its not sure which kind of type you can expect at a certain position in the document, then the isArray, isObject (...) functions are helpful to find out which version of the document you find. For example I've seen differences with php writing encode_json from an array, sometimes the comments are encoded as an array, but mostly as an object. QJsonObject also offers you an STL like iterator interface, which lets you iterate over the contents of an JSON object.

Writing JSON Values

Writing is even easier. Elements can be put into an Object by simply using the [] operator again, QJsonArray has an append method:

    QJsonObject obj;//root object
    obj["voteid"] = QString("%1");//(1)
    QJsonArray talks;//(2)

    while(query.next())//load all data from the database
    {
        QJsonObject talk;//(3)
        talk["id"] = query.value(0).toInt();//(4)
        talk["title"] = query.value(1/*"title"*/).toString();;
        talk["desc"] = query.value(2/*"desc"*/).toString();
        ...
        talks.append(talk);//(5)
    }
    obj["talks"] = talks;//(6)
    return QJsonDocument(obj).toJson(QJsonDocument::Compact);//(7)

This method loads all talks from the database and returns a QString containing the whole JSON file:

  1. voteid is a unique identifier which is later replaced for each voting file, in this spot its only a placeholder: %1.
  2. QJsonArray which is the container for all talks
  3. JSON Object for a single talk
  4. Storing all elements from the database into the current object
  5. Pushing the current object into the JSON Array
  6. Storing the talks into the root object
  7. Creating a QJsonDocument from the root object and obtaining its string representation.

So, the Qt5 JSON API is mostly straight forward, except that you sometimes have to test for errors when reading. Qt does not use exceptions, so error checking is vital.