c++jsonqtdeserializationmoc

setProperty() returning false


I'm trying to convert a QVariantMap to a custom class derived from QObject but I'm getting the return value of false from setProperty() when it comes to set the property of my enum type. Code goes below:

The MessageHeader.h file:

// deserialization class header
class MessageHeader : public QObject
{
    Q_OBJECT

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

    enum class MessageType
    {
        none = 0,
        foo = 1,
        baa = 2
    };

    Q_ENUM(MessageType)
    Q_PROPERTY(MessageType type READ getType WRITE setType)
    Q_PROPERTY(int ContentLength READ getContentLength WRITE setContentLength)

    void setType(MessageType type);
    void setContentLength(int ContentLength);
    MessageType getType();
    int getContentLength();
    QString toString();

    MessageType type = MessageType::none;
    int ContentLength = 0;
};

The MessageHeader.cpp file:

MessageHeader::MessageHeader(QObject *parent)
    : QObject(parent)
{
}

MessageHeader::~MessageHeader()
{
}

MessageType MessageHeader::getType()
{
    return type;
}

int MessageHeader::getContentLength()
{
    return ContentLength;
}

void MessageHeader::setType(MessageType type)
{
    this->type = type;
}

void MessageHeader::setContentLength(int ContentLength)
{
    this->ContentLength = ContentLength;
}

QString MessageHeader::toString()
{
    return QString("NOT IMPLEMENTED YET");
}

And the deserialize function template helper:

  template<typename T>
    T* Deserialize(const QString &json)
    {
        bool status = false;

        QJson::Parser parser;
        QVariantMap map = parser.parse(json.toUtf8(), &status).toMap();

        if(!status)
            return NULL;

        T *obj = new T(); //don't worry about this, I'll rather take this from paramters once this is working
        QObject *p = (QObject *) obj; // cast done so that I see setProperty() method
        for(QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
        {
            const char *name = iter.key().toLatin1();
            const QVariant value = iter.value();
            qDebug() << "setting " << name << "=" << value;
            // the issue goes below. Here setProperty() return false.
            // At this point, name = 'type' and value = 2
            assert(p->setProperty(name, value));
        }
    //QJson::QObjectHelper::qvariant2qobject(map, obj);
    return obj;
}

The JSON input string to above function is like this:

"{\"ContentLength\": 100, \"type\": 2}"

The enum type is registered in the main funcction before anything else:

qRegisterMetaType<MessageType>("MessageType");

And here's the QJson library used in this example. I build it on Windows with this .pro file

EDIT:

I just found that the type property can't be find by indexOfProperty()

qDebug() << "id = " << meta->indexOfProperty(name); // print -1, name = 'type'

Solution

  • The enum property can only be set if the variant type is either a QString, QInt or QUInt as could be seen here. So to successfully set the enum property, the variant needs to be one of these types and nothing else. QJson parses any unsigned integers as QULongLong as can be seen here, line 84. So one way is to fork QJson and modify the code so the integer values are converted to QInt and QUInt or read/write the enum values as strings.

    Also, putting statements within an assert is not a good idea, but I assume you just wrote that code trying to figure out the problem.

    Just as a side note, according to Qt documentation,

    [qRegisterMetaType] is useful only for registering an alias (typedef) for every other use case Q_DECLARE_METATYPE and qMetaTypeId() should be used instead.

    so replacing qRegisterMetaType<MessageHeader::MessageType>("MessageType") with Q_DECLARE_METATYPE(MessageHeader::MessageType) in your header would be a reasonable move.