Friday, July 17, 2020

Serialize/Deserialize JSON Representations to Qt Objects (QObject's) in C++

Calls to HTTP servers are very frequent nowadays, and JSON is frequently used when dealing with structured data. When I write applications in Java/Kotlin, I always use retrofit2 with gson integration, or similar approaches. It is very comfortable to be able to serialize/deserialize to Java objects automatically. I tried to find something similar for Qt, but I couldn't find much. JSON support in Qt is good, but does not seem to be able to map QOject's to QJSonObject. I also could not find other C++ libraries doing this. Probably the difficulty of implementing reflection in C++ would make the implementation a bit convoluted. A good candidate I found is this: https://github.com/Loki-Astari/ThorsSerializer, which seems not to require much boilerplate code, not too verbose and no generated code.

However, Qt includes the meta object system (https://doc.qt.io/qt-5/metaobjects.html), and therefore I tried to use it to get the desired result. In a couple of hours I got to a pretty decent result: https://github.com/carlonluca/lqobjectserializer. It is far from perfect, but it seems to work. Have a look at the readme or the unit tests to know more.
The draft is free to use: you can contribute, report bugs etc...

For instance, a JSON object like this:

{"menu": {
    "header": "SVG Viewer",
    "items": [
        {"id": "Open"},
        {"id": "OpenNew", "label": "Open New"},
        null,
        {"id": "ZoomIn", "label": "Zoom In"},
        {"id": "ZoomOut", "label": "Zoom Out"},
        {"id": "OriginalView", "label": "Original View"},
        null,
        {"id": "Quality"},
        {"id": "Pause"},
        {"id": "Mute"},
        null,
        {"id": "Find", "label": "Find..."},
        {"id": "FindAgain", "label": "Find Again"},
        {"id": "Copy"},
        {"id": "CopyAgain", "label": "Copy Again"},
        {"id": "CopySVG", "label": "Copy SVG"},
        {"id": "ViewSVG", "label": "View SVG"},
        {"id": "ViewSource", "label": "View Source"},
        {"id": "SaveAs", "label": "Save As"},
        null,
        {"id": "Help"},
        {"id": "About", "label": "About Adobe CVG Viewer..."}
    ]
}}

can be deserialized to a QObject simply by defining the classes (macros here are inherited by my other project https://github.com/carlonluca/lqtutils, but this is not mandatory):

L_BEGIN_CLASS(Item)
L_RW_PROP(QString, id, setId, QString())
L_RW_PROP(QString, label, setLabel, QString())
L_END_CLASS

L_BEGIN_CLASS(Menu)
L_RW_PROP(QString, header, setHeader)
L_RW_PROP_ARRAY_WITH_ADDER(Item*, items, setItems)
L_END_CLASS

L_BEGIN_CLASS(MenuRoot)
L_RW_PROP(Menu*, menu, setMenu, nullptr)
L_END_CLASS

and by writing these few lines:

LDeserializer<MenuRoot> deserializer(factory);
QScopedPointer<MenuRoot> g(deserializer.deserialize(jsonString));

Please leave a comment if you know of other tools in this context, serializing and deserializing JSON is very frequent.
Bye! ;-)