将QObject的实例转换为JSON

时间:2018-04-17 00:29:54

标签: json qt qvariant

我有一些代码用于将任意QObject子类转换为JSON。如果它们是指向子类的指针,我能够转换它们,但我很好奇是否可以转换实例(假设子类实现了一个复制构造函数)。是否有一些疯狂的方法来使用模板或QMetaType提供的类型信息来复制QObject子类的实例,而不知道它是什么? ToJson代码位于不了解子类的类中。

我认为可能可以使用QMetaType::create或类似的东西,但我还没有能够弄清楚如何实际复制子类实例的属性。

这是我的转换代码:

QJsonValue ToJson(QVariant value){
    switch(value.type()){
    case QVariant::Int:
    case QVariant::Double:
        return value.toDouble();

    ////Other cases, etc...
    case QVariant::UserType:
        QObject* obj_ptr = qvariant_cast<QObject*>(value);
        if(obj_ptr) // value was originally a pointer to a QObject, works correctly
            return ToJson(obj_ptr);
        else { // value was orginally an instance of a QObject subclass
            std::string t = value.typeName(); //returns "MyQObject"
            int id = QMetaType::type(t.c_str()); //returns the id of the derived class
            void* v = QMetaType::create(id, &value); //passing &value does nothing
            obj_ptr = static_cast<QObject*>(v);
            return ToJson(obj_ptr); //works, but resulting fields are all default 
        }
    }
}

QJsonObject ToJson(QObject* o){
    QJsonObject obj;

    auto mo = o->metaObject();
    for (int i = mo->propertyOffset(); i < mo->propertyCount(); ++i){
        QVariant value = o->property(mo->property(i).name());
        obj[mo->property(i).name()] = ToJson(value);
    }
    return obj;
}

示例代码用例:

qRegisterMetaType<MyQObject>();
MyQObject obj;
obj.db = 11.1;

QVariant test1 = QVariant::fromValue(obj);
QVariant test2 = QVariant::fromValue(&obj);
QJsonValue v1 = ToJson(test1);  // default constructed values
QJsonValue v2 = ToJson(test2);  // db = 11.1

示例QObject子类:

class MyQObject : public QObject {
    Q_OBJECT
    Q_PROPERTY(double DB MEMBER db)
    Q_PROPERTY(int I MEMBER i)
    public:
        MyQObject();
        MyQObject(const MyQObject& other) : QObject() { 
            i = other.i;
            db = other.db;
        }
        int i = 50;
        double db = 1.5;
};
Q_DECLARE_METATYPE(MyQObject)

有没有办法处理上面test1所示的案例?

2 个答案:

答案 0 :(得分:0)

长话短说:不。无法按容器或QObject按值存储QVariant

Qt禁止QObjects和所有继承类的副本。强制Q_OBJECT宏也将在新定义的类中禁用任何复制构造函数。

您在MyObject类中定义的复制构造函数缺少基类构造函数调用。如果QObject有一个复制构造函数,它将是这样的:

MyQObject(const MyQObject& other) :
    QObject(other) // this will NEVER compile
{ 
   i = other.i;
   db = other.db;
}

编译器可能会给你一个警告,但允许你有这样的构造函数,即使它会导致未定义的行为,或者切片每次MyObject的实例按值传递。

此外,Qt docs声明如下:

  

存储在各种容器中的值可以是任何可分配的   数据类型。要限定,类型必须提供默认构造函数,a   复制构造函数和赋值运算符。这涵盖了大多数数据   您可能希望存储在容器中的类型,包括基本类型   类型,如int和double,指针类型和Qt数据类型,如   QString,QDate和QTime,但它不包括QObject或任何QObject   子类(QWidget,QDialog,QTimer等)。

因此,您不能将QObject和派生类存储在Qt容器中,除非您将它们存储为指针,因为QObject的副本已被设计禁用。

此外,如果你想利用多态行为,你必须使用指针,即使我没有明确需要在你的代码中强制转换为派生类。如果你真的需要在某个地方求助,你可以考虑让你的ToJson成为模板功能。

答案 1 :(得分:0)

有一个解决方案,但请谨慎使用,因为它只适用于以下情况:

  • 有问题的类主要是数据存储类
  • 如果他们没有从QObject
  • 继承,那么这些课程将完全可以复制
  • 最重要的是,这个类继承自QObject的唯一理由是它可以具有元属性。

如果您的代码因为获取元信息之外的任何原因而将该类用作QObject,那么如果您尝试按值存储它,则几乎肯定会错误地使用它(如G. Giordano所述)他们的回答)。

除了滥用注意事项之外,为了JSON-ify QVariant按值存储QObject子类,您可以使用QMetaType::create方法并将其传递给用户类型ID和{ {1}}。

示例:

yourQVariant.constData()