将 QObject 的实例转换为 JSON



我有一些代码用于将任意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说明的情况?

长话短说:没有。无法将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文档指出以下内容:

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

因此,除非将QObject类和派生类存储为指针,否则您无法将它们存储为 Qt 容器,因为设计禁用了 QObjects 的副本。

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

有一个解决方案,但要谨慎,因为它仅在以下情况下才合理/适用:

  • 有问题的类主要是数据存储类
  • 有问题的类如果不继承自QObject,则完全可以复制
  • 最重要的是,你让类从QObject继承的唯一原因是它可以具有元属性。

如果你的代码出于获取元信息以外的任何原因将该类用作QObject,那么如果您尝试按值存储它,您几乎可以肯定是错误地使用它(正如 G. Giordano 在他们的答案中所解释的那样)。

撇开误用注意事项不谈,为了对按值存储QObject子类的QVariant进行 JSON 化,您可以使用QMetaType::create方法并向其传递用户类型 id 和yourQVariant.constData()

例:

MyQObject obj;
obj.db = 11.1;
QVariant value = QVariant::fromValue(obj);
std::string t = value.typeName();
int id = QMetaType::type(t.c_str());
void* v = QMetaType::create(id, value.constData());
obj_ptr = static_cast<QObject*>(v);
QJsonValue json = ToJson(obj_ptr); //json contains db = 11.1

相关内容

  • 没有找到相关文章

最新更新