Git Product home page Git Product logo

Comments (6)

OgreTusk avatar OgreTusk commented on July 20, 2024

I just realized that QVariant actually lets me convert back and forth without any issues however I must be missing a step for QtJsonSerializer. Code snippet below to provide an example.

foo.hpp

class Foobar
{
    Q_GADGET

    Q_PROPERTY(QString Name MEMBER Name)

public:
    QString Name;
};
Q_DECLARE_METATYPE(Foobar)

class FoobarList : public QList<Foobar>
{
    Q_GADGET

public:
    static void registerMetaType()
    {
        QtJsonSerializer::JsonSerializer::registerListConverters<Foobar>();
    }

    FoobarList(const QList<Foobar> &a = QList<Foobar>())
        : QList(a)
    {}
};
Q_DECLARE_METATYPE(FoobarList)

foo.cpp

namespace
{
    void registerMetaType()
    {
        FoobarList::registerMetaType();
    }
} // namespace
Q_COREAPP_STARTUP_FUNCTION(registerMetaType)

main.cpp

#define JSON_PRINT(x) QJsonDocument(x).toJson().constData()

QtJsonSerializer::JsonSerializer s;

FoobarList a;

// populating
for (int i = 0; i < 3; ++i)
{
    a.append(Foobar{
        .Name = QString("Foo%1").arg(i, 4, 10, QLatin1Char('0')),
    });
}

auto printDebug = [&s](const FoobarList &o, const QString &typeName) {
    qDebug() << typeName << o.length() << JSON_PRINT(s.serialize(o));

    for (int i = 0; i < o.length(); ++i)
        qDebug() << QString("%1: %2").arg(i).arg(o.at(i).Name);
};

{ // QVariant
    QVariant v;
    v.setValue(a);
    FoobarList o = v.value<FoobarList>();
    printDebug(o, "QVariant"); // success
}

{ // QtJsonSerializer
    FoobarList o = s.deserialize<FoobarList>(s.serialize(a));
    printDebug(o, "QtJsonSerializer"); // failure
}

Output

"QVariant" 3 {
}

"0: Foo0000"
"1: Foo0001"
"2: Foo0002"

"QtJsonSerializer" 0 {
}

from qtjsonserializer.

Skycoder42 avatar Skycoder42 commented on July 20, 2024

QtJsonSerializer detects that your Foobar is a Q_GADGET and thus makes a property based serialization. The list is then just detected as such - a generic list of something, in this case the Foobar, which gets serialized as object.

To archive your goal of having a simple list, you have 2 options:

  1. Create a custom type converter for Foobar, that serializes it to a string instead of a json object
  2. Create a custom type converter for FoobarList, that creates a string list.

Creating a custom type converter is not that hard, simply implement the QtJsonSerializer::TypeConverter class and register it via QtJsonSerializer::JsonSerializer::addTypeConverter. Check the Documentation for more details.

from qtjsonserializer.

OgreTusk avatar OgreTusk commented on July 20, 2024

Hello,

I've been working on adding the converted as suggested, it seems to work somewhat but... I can't figure out if some of this stuff is intended or not. Code below.

class FoobarListConverter : public QtJsonSerializer::TypeConverter
{
    Q_GADGET

public:
    QByteArray name() const override
    {
        return staticMetaObject.className();
    }
    QCborTag tag() const
    {
        static QCborTag r(QCborValue::fromVariant(QVariant::fromValue(FoobarList())).tag());
        return r;
    }
    bool canConvert(int metaTypeId) const override
    {
        return metaTypeId == qMetaTypeId<FoobarList>();
    }
    QList<QCborValue::Type> allowedCborTypes(int metaTypeId, QCborTag tag) const override
    {
        Q_UNUSED(metaTypeId)
        Q_UNUSED(tag)
        return {QCborValue::Array};
    }
    QCborValue serialize(int propertyType, const QVariant &value) const override
    {
        QCborArray r;

        for (const auto &v : value.value<FoobarList>())
        {
            // we know Foobar is properly serialized, so let's make use of that
            r.append(helper()->serializeSubtype(qMetaTypeId<Foobar>(), QVariant::fromValue(v)));
        }

        // add extra for the sake of it
        r.append(helper()->serializeSubtype(qMetaTypeId<Foobar>(), QVariant::fromValue(Foobar{
            .Name = "FoobarListConverter's Visit Card",
            .Text = "It reads: \"FoobarListConverter was here.\"",
        })));
        
        return QCborValue(r);
    }
    QVariant deserializeCbor(int propertyType, const QCborValue &value, QObject *parent) const override
    {
        Q_UNUSED(propertyType)
        Q_UNUSED(parent)

        qDebug() << "deserializeCbor" << value.type() << value;

        FoobarList r;

        for (const auto &v : value.toArray())
        {
            r.append(helper()->deserializeSubtype(qMetaTypeId<Foobar>(), v, nullptr).value<Foobar>());
        }

        return QVariant::fromValue(r);
    }
};

The serialization does work, but in a strange way for the return value. In main.cpp, if you use serialize, like below:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    QtJsonSerializer::JsonSerializer s;
    /**
    * I have to keep in mind addJsonTypeConverter
    * that is NOT a global register.
    * 
    * Might need a global instance of the serializer
    * for this.
    */
    s.addJsonTypeConverter<FoobarListConverter>();

    FoobarList a;
    a.append(Foobar{
        .Name = "My Name!",
    });

    /**
     * The method QtJsonSerializer::JsonSerializer::serialize
     * always seems to return a QJsonObject for some reason
     * despite the documentation stating otherwise and returning
     * a QJsonValue, not a QJsonObject. Is the QJsonValue wrapped
     * the QJsonObject itself? It seemed empty.
     *
     * To add to the issue, QtJsonSerializer::JsonSerializer::deserialize
     * also only accepts QObjects which is very odd considering that once
     * again, the document tell us that it accept a QJsonValue.
     *
     * Doc: https://skycoder42.github.io/QtJsonSerializer/class_qt_json_serializer_1_1_json_serializer.html#adde044d01cf8e1705ee672562495ad6f
     */
    auto i = s.serialize(a); // ALWAYS recover a QJsonObject no matter what I do
    qDebug() << "Is the object empty?" << i.isEmpty();
    
    /**
     * This method work.
     */
    auto j = s.serializeGeneric(QVariant::fromValue(a)); // recover QJsonValue which contains QJsonArray
    qDebug() << "QJsonArray" << JSON_PRINT(std::get<QJsonValue>(j).toArray());

    auto b = s.deserialize(std::get<QJsonValue>(j).toArray(), qMetaTypeId<FoobarList>()).value<FoobarList>(); // HELP
    for (int i = 0; i < b.length(); ++i)
    {
        const auto &v = b.at(i);
        qDebug() << i << v.Name << v.Text << v.Bool;
    }
    return 0;
}

Console:

Is the object empty? true
QJsonArray [
    {
        "bool": false,
        "name": "My Name!",
        "text": ""
    },
    {
        "bool": false,
        "name": "FoobarListConverter's Visit Card",
        "text": "It reads: \"FoobarListConverter was here.\""
    }
]

Are serialize and deserialize intended to only use QJsonObject? This seems unintended as provided by the documentation.

from qtjsonserializer.

OgreTusk avatar OgreTusk commented on July 20, 2024

Hiya,

I understand my issue. But it seems like a very odd behaviour for serialize and deserialize, the document tell us that it takes/return a Json, not a Json object.

In any case, for the sake of completion, working conversion:

// QJsonArray i = s.serialize(a).toArray(); // doesn't work, always return a QJsonObject 
QJsonValue i = s.serialize(QVariant::fromValue(a)); // works, recover QJsonArray as QJsonValue rather than a QJsonObject that has an immediate conversion to QJsonValue
qDebug() << JSON_PRINT(i.toArray());

// FoobarList k = s.deserialize<FoobarList>(i); // can't take QJsonValue or QJsonArray as parameters; only QJsonObject
FoobarList k = s.deserialize(i, qMetaTypeId<FoobarList>()).value<FoobarList>(); // works, obviously
for (int i = 0; i < k.length(); ++i)
{
    const auto &v = k.at(i);
    qDebug() << i << v.Name << v.Text << v.Bool;
}

Thanks a lot!

from qtjsonserializer.

Skycoder42 avatar Skycoder42 commented on July 20, 2024

About those points:

  1. FoobarListConverter::tag is not really needed, you can simply return static_cast<QCborTag>(-1) from that method
  2. You can actually register the converter globally. Use JsonSerializer::addJsonTypeConverterFactory for this.
  3. The return value stuff is because your FoobarList is a Q_GADGET. The serializer uses some generic stuff to dynamically detect what kind of json certain data would be. Normally, list classes are detected as such and are matched to a QJsonArray. But since your list is a gadget, that one is detected instead and an object is expected/returned, as that is the "default" for gadgets. You have already figured out the solution: use the QVariant based methods instead, and you're good... I know this is sligthly inconvenient, but this design seemed like a good idea at the time...

from qtjsonserializer.

OgreTusk avatar OgreTusk commented on July 20, 2024

Thanks a lot, I didn't realize that Q_GADGET would force a map/array to be considered as an object. Thank you again!

from qtjsonserializer.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.