Some days ago I introduced the possibility to serialize a QObject instance to JSON. Today I’m going to show you the opposite operation: initializing a QObject using a JSON object.

I refactored a bit my latest changes: I created a new class called QObjectHelper that provides the methods required to convert a QObject instance to a QVariantMap and vice-versa.

This class can be used in conjunction with the Serializer and Parser classes to serialize and deserialize QObject instances to and from JSON.

Let me show a quick example, suppose the declaration of Person class looks like this:

class Person : public QObject
{
  Q_OBJECT

  Q_PROPERTY(QString name READ name WRITE setName)
  Q_PROPERTY(int phoneNumber READ phoneNumber WRITE setPhoneNumber)
  Q_PROPERTY(Gender gender READ gender WRITE setGender)
  Q_PROPERTY(QDate dob READ dob WRITE setDob)
  Q_ENUMS(Gender)

public:
    Person(QObject* parent = 0);
    ~Person();

    QString name() const;
    void setName(const QString& name);

    int phoneNumber() const;
    void setPhoneNumber(const int  phoneNumber);

    enum Gender {Male, Female};
    void setGender(Gender gender);
    Gender gender() const;

    QDate dob() const;
    void setDob(const QDate& dob);

  private:
    QString m_name;
    int m_phoneNumber;
    Gender m_gender;
    QDate m_dob;
};

From QObject to JSON

The following code will serialize an instance of Person to JSON :

Person person;
person.setName("Flavio");
person.setPhoneNumber(123456);
person.setGender(Person::Male);
person.setDob(QDate(1982, 7, 12));

QVariantMap variant = QObjectHelper::qobject2qvariant(&person);
Serializer serializer;
qDebug() << serializer.serialize( variant);

The generated output will be:

{ "dob" : "1982-07-12", "gender" : 0, "name" : "Flavio", "phoneNumber" : 123456 }

From JSON to QObject

Suppose you have the following JSON data stored into a QString:

{ "dob" : "1982-07-12", "gender" : 0, "name" : "Flavio", "phoneNumber" : 123456 }

The following code will initialize an already allocated instance of Person using the JSON values:

Parser parser;
QVariant variant = parser.parse(json);

Person person;
QObjectHelper::qvariant2qobject(variant.toMap(), &person);

A new release

These changes have been included inside the new release of QJson: 0.7.0.

Packages for openSUSE are building right now.

Tags: , ,
19 Responses to “QJson: from QObject to JSON and vice-versa”
  1. Jason says:

    What about nested types?

    • Flavio says:

      I’m going to provide some examples showing serializing operations with nested types.

      • Jason says:

        Cool! I’m not sure what you have in mind, but from looking at this code:

        QVariant value = object->property(name);
        result[QLatin1String(name)] = value;

        , it looks like the user will have to manually convert those map values that are still qobjects into more qvariantmaps. Isn’t there a recursive way to do this automatically? The question is, what types of qobjects warrant this type of property treatment, and which do not?

        But it looks like you’ve already answered that question:
        Line 144: //TODO: catch other values like QImage, QRect, …

        Maybe the recursive logic aught to be included here (or included in a overloaded seralize() for qobjects). That is to say, all qobjects that cannot be converted like the other qvariants get converted recursively using the qt property system.

        This way you could call seralize on any qobject and have it resolve the properties or JSON equivalants recursively and automatically.

        Or am I confused about something?

  2. Thiago Macieira says:

    Please remember not to use classes under Q + Capital letter in the global namespace. Those are reserved by Qt itself. Imagine what would happen if QJson were to be included in Qt. :-)

  3. Have you got a plan to create JSON member names that are not valid c++ symbols, for example “date-of-birth” or “delete”? The c++ compiler will choke on this when it is used as a property name. Is this a real problem?

    I read rfc4627 and there are no restrictions on member names to make them compiler-friendly.

    I encountered this problem when making Qt bindings for the NetworkManager DBUS interface – since they were using manually created variant maps to represent settings with no real object backing it, the designers had no reason not to use ‘-’ liberally in member names. I had to create a mapping system to turn legal c++ symbols back into what the server expects on the bus.

    Perhaps you could optionally supply a mapping to the Serializer and Parser classes allowing them to perform this?

  4. Shyru says:

    Very great, I will try that out soon.
    Do you have any timeframe on the name change? (Could be a bit nasty to convert a big project later)

  5. Jason says:

    As we discussed, I’ve implemented the serializer side of things for the recursive/nested implementation. What remains is the parser side of things. Also some duplicate code needs to be taken care of. The logic for the qobject serialization has been taken out of qobjecthelper and put directly in the serializer class. Eventually the same would be done for the parser, and qobjecthelper would no longer be necessary. Here’s the patch:

    http://data.zx2c4.com/recursiveqjsonserializer.diff

    If you like it, let me know, and I’ll commit it myself.

  6. Rno says:

    Hello,

    It’s probably already discussed but I’m looking at the code and it seems that the serialization is done recursively. It may be a problem in case of a huge depth, like serializing a tree, right?

  7. Diederik van der Boor says:

    API question: Why do you have to create a “Parser parser” or “Serializer serializer” object first?

    Can’t the API look like this?

    QVariant variant = QtJson::decode( jsonString );

    QString json = QtJson::encode( qvariant )

    e.g. using static methods.

  8. C++Newbie says:

    void setPhoneNumber(const int phoneNumber);

    isn’t the const needless? I mean it’s a simple integer which gets copied, not a reference.

  9.  
Leave a Reply