QtJsonSerializer icon indicating copy to clipboard operation
QtJsonSerializer copied to clipboard

How to deserialize derived classes that use none-default constructors

Open useful-friend opened this issue 4 years ago • 2 comments

Hello there, I was trying to deserialize some polymorphic classes which use none-default constructors by creating my own QJsonTypeConverter with the help of discussion about it in issue #22. let's say we have class Base and class Derived. I faced a problem with the canConvert() function. when I want to deserialize the Derived class canConvert() is called with the metaTypeId of Base * instead of Derived *. Am I doing something wrong? How can I solve that problem?

useful-friend avatar Mar 18 '20 18:03 useful-friend

First, make sure to read the documentation about this library treats polymorphism: https://skycoder42.github.io/QtJsonSerializer/class_qt_json_serializer_1_1_serializer_base.html#a18d5fc879178e3d3ab9094de4f3277e2

As stated there, the serializer should add a special "@class" property that holds the actual type. This property is written and read/interpreted by the object serializer itself - this means if you have a custom serializer, you have to duplicate that behaviour.

Getting the can convert for the base class is correct, as from C++, you request the deserialization of a Base object. Your serializer should be able to detect that it is actually a Derived and do the serialization, as generally there is no guarantee that the object being deserialized is actually of the Derived type. If you know that beforehand, you could just deserialize it as Derived directly and don't need polymorphism.

I hope this helps you with your problem. If not, maybe post the code of your custom converter here, and I can take a look at it and give you some ideas on how to proceed.

Skycoder42 avatar Mar 21 '20 10:03 Skycoder42

I'm using version: 3.3.1-3

The serializer works fine with most of my polymorphic classes except the one I mentioned above.

I've used the Q_CLASSINFO("polymorphic", "true") in the Base class declaration. Let's say we have the following code snippet. Note: "..." means that the code in the QJsonObjectConverter is used without any changes.

class Base : public QObject
{
    Q_OBJECT
    Q_CLASSINFO("polymorphic", "true")
public:
    Base(QObject *parent = nullptr) {}
}


class Derived : public Base
{
    Q_OBJECT
public:
    Q_INVOKABLE  Derived(int a, QObject *parent = nullptr) : Base(parent) {}
}

class Derived2 : public Base
{
    Q_OBJECT
public:
    Q_INVOKABLE  Derived2(int a, int b, QObject *parent = nullptr) : Base(parent) {}
}

First I need to do something to override the default object instantiation by adding a type converter. right? so I created two new type converter with the following codes:

class Q_JSONSERIALIZER_EXPORT QJsonCustomConverter : public QJsonTypeConverter
{
   int m_number;
public:
    QJsonCustomConverter(int n) : m_number(n) {}
    bool canConvert(int metaTypeId) const override;
    ...
    ...
    ...
}

definition:

bool QJsonCustomConverter::canConvert(int metaTypeId) const
{
    if (metaTypeId != QMetaType::type("Derived*"))
        return false;
    auto flags = QMetaType::typeFlags(metaTypeId);
    return (flags.testFlag(QMetaType::PointerToQObject) ||
            flags.testFlag(QMetaType::SharedPointerToQObject) ||//weak ptr cannot be constructed
            flags.testFlag(QMetaType::TrackingPointerToQObject));
}
...
...
QVariant QJsonCustomConverter::deserialize(int propertyType, const QJsonValue &value, QObject *parent, const QJsonTypeConverter::SerializationHelper *helper) const
{
...
...
auto object = metaObject->newInstance(Q_ARG(int, m_number), Q_ARG(QObject*, parent));
    if(!object) {
        throw QJsonDeserializationException(QByteArray("Failed to construct object")
    }
...
...
}

and for the second class:

class Q_JSONSERIALIZER_EXPORT QJsonCustomConverter2 : public QJsonTypeConverter
{
   int m_number;
   int m_number2;
public:
    QJsonCustomConverter2(int n, int n2) : m_number(n), m_number2(n2) {}
    bool canConvert(int metaTypeId) const override;
    ...
    ...
    ...
}

definition:

bool QJsonCustomConverter::canConvert(int metaTypeId) const
{
    if (metaTypeId != QMetaType::type("Derived2*"))
        return false;
    auto flags = QMetaType::typeFlags(metaTypeId);
    return (flags.testFlag(QMetaType::PointerToQObject) ||
            flags.testFlag(QMetaType::SharedPointerToQObject) ||//weak ptr cannot be constructed
            flags.testFlag(QMetaType::TrackingPointerToQObject));
}
...
...
QVariant QJsonCustomConverter::deserialize(int propertyType, const QJsonValue &value, QObject *parent, const QJsonTypeConverter::SerializationHelper *helper) const
{
...
...
auto object = metaObject->newInstance(Q_ARG(int, m_number), Q_ARG(int, m_number2), Q_ARG(QObject*, parent));
    if(!object) {
        throw QJsonDeserializationException(QByteArray("Failed to construct object")
    }
...
...
}

I added those converters and faced the problem mentioned in the previous post in deserialization and none of the converters is used because the canConvert function returns false.

useful-friend avatar Mar 25 '20 20:03 useful-friend