找回密码
 立即注册
首页 业界区 业界 QT 实现 C++ 数据类与 json 的转换

QT 实现 C++ 数据类与 json 的转换

注思 2025-6-2 22:25:00
QT 提供了 QJsonDocument、QJsonObject、QJsonArray、QJsonValue 等类用于 JSON 的解析和转换。QJsonValue 支持的数据类型包括:bool、double、string、array、object、null。但是,对于 QRectF、QLineF、QColor 等类以及用户自定义数据类,QJsonObject 就无法转换,更无法生成可读的字符串。此时,需要我们自己来实现转换并定义转换后的 JSON 格式。
上篇文章,借助 QT 的反射机制实现数据类的序列化 实现了数据类的序列化,简化了数据类的编写,同时提供了转换为 JSON 的基础。通过元对象系统很容易找到我们通过宏 JSONFIELD 记录的需要序列化的字段,因为记录序列化的方法被导出并标记为 JSON_FLAG 。使用反射机制就可以找到所有记录序列化字段的方法,获取字段名后通过 getValue()、setValue() 即可获取或设置字段值。
  1. // serializable.h
  2. #define JSONFIELD(field, alias, ...) \
  3. using __type_##field = decltype(field) ;\
  4. Q_PROPERTY(__type_##field field READ get##alias WRITE set##alias) \
  5.     public: \
  6.     Q_INVOKABLE JSON_FLAG inline QMap<QString, QString> __get##alias##Info__(){ \
  7.         QMap<QString, QString> info; \
  8.         info["name"] = #field; \
  9.         info["alias"] = #alias; \
  10.         info["args"] = QString(#__VA_ARGS__); \
  11.         return info; \
  12.     } \
  13.     inline __type_##field get##alias() const { return field; } \
  14.     inline void set##alias(const __type_##field &value) { \
  15.             field = value; \
  16.     }
复制代码
定义通用的 JSON 接口

JSON 接口主要定义 4 个功能接口:1. 将数据类转换为 QJsonObject 对象;2. 将数据类转换为字符串;3. 将字符串解析为指定的数据类;4. 将 QJsonObject 转换为指定的数据类;
系统允许多个接口实现类,但是全局只允许有一个实例,用于整个工程的 JSON 转换。所以声明了一个全局的 EasyJson 对象 EASYJSON。
  1. #include "serializable.h"
  2. #include <QJsonObject>
  3. class EasyJson{
  4. public:
  5.     EasyJson(){}
  6.     ~EasyJson(){}
  7.     virtual QJsonObject toJson(const Serializable &obj) = 0;
  8.     virtual QString toJsonString(const Serializable &obj) = 0;
  9.     virtual QVariant parseObject(QJsonObject json, QMetaType typeName) = 0;
  10.     virtual QVariant parseObject(QString json, QMetaType typeName) = 0;
  11. };
  12. extern EasyJson *EASYJSON;
复制代码
EasyJson 的实现类

实现类直接继承 EasyJson 类,完成接口代码即可。QT 中数据类转换为 JSON 的难点在于 QRectF、QSizeF 等类的转换,以及 Serializable 作为数据类字段时的转换。为了便于 QT 内部封装类的解析,需要将解析方法单独封装为一个工具类,这样便于后期添加和修改。工具类的实现见 variantutil.h 文件。
  1. // easyjsonimpl.h
  2. #include "easyjson.h"
  3. class EasyJsonImpl: public EasyJson
  4. {
  5. public:
  6.     EasyJsonImpl();
  7.     // EasyJson interface
  8. private:
  9.     QJsonObject toJson(const Serializable &obj) override;
  10.     QString toJsonString(const Serializable &obj) override;
  11.     QVariant parseObject(QJsonObject json, QMetaType typeName) override;
  12.     QVariant parseObject(QString json, QMetaType typeName) override;
  13. };
复制代码
为了便于切换不同的 JSON 实现类,EASYJSON 对象的创建与否需要通过指定的宏来判断一下。如 EasyJsonImpl 源码中规定只有定义了 EASY_JSON_DEFAULT 才会实例化 EasyJsonImpl。
这样在 .pro 文件中添加 DEFINES += EASY_JSON_DEFAULT 即可启用该实现类。如果有不同的实现类,定义不同的宏即可。
  1. // easyjsonimpl.cpp
  2. #include "easyjsonimpl.h"
  3. #include "variantutil.h"
  4. #include <QObject>
  5. #include <QMetaObject>
  6. #include <QMetaProperty>
  7. #include <QColor>
  8. #include <QJsonArray>
  9. #include <QLineF>
  10. #include <QPointF>
  11. #include <QRectF>
  12. #include <QSizeF>
  13. #include <QJsonDocument>
  14. #ifdef EASY_JSON_DEFAULT
  15. EasyJson *EASYJSON = new EasyJsonImpl();
  16. #endif
  17. EasyJsonImpl::EasyJsonImpl() {}
  18. QJsonObject EasyJsonImpl::toJson(const Serializable &obj)
  19. {
  20.     QJsonObject json;
  21.     Serializable *objPtr = const_cast<Serializable*>(&obj);
  22.     const QMetaObject *metaInfo = obj.getMetaInfo();//obj.metaObject();
  23.     do{
  24.         int count = metaInfo->methodCount();
  25.         for(int i=0; i< count; i++){
  26.             if (QString(metaInfo->method(i).tag()).compare("JSON_FLAG") == 0){
  27.                 QMap<QString, QString> jsonInfo;
  28.                 jsonInfo = objPtr->invokeMethod<QMap<QString, QString>>(metaInfo, i);
  29.                 QString alias = jsonInfo["alias"];
  30.                 QVariant value = objPtr->getValue(jsonInfo["name"]);
  31.                 QMetaType type = value.metaType();
  32.                 // 对 Serializable 子类递归转换
  33.                 if (type.id() > QMetaType::User) {
  34.                     auto valueMeta = type.metaObject();
  35.                     auto classInfo = valueMeta->classInfo(valueMeta->indexOfClassInfo("base"));
  36.                     if (QString("Serializable").compare(classInfo.value()) == 0) {
  37.                         json.insert(alias, toJson(*reinterpret_cast<const Serializable*>(value.constData())));
  38.                         continue;
  39.                     }
  40.                 }
  41.                 // 转为json对象
  42.                 json.insert(alias, VariantUtil::toJsonValue(value));
  43.             }
  44.         }
  45.         metaInfo = metaInfo->superClass();
  46.     }while(metaInfo != nullptr);
  47.     return json;
  48. }
  49. QString EasyJsonImpl::toJsonString(const Serializable &obj)
  50. {
  51.     QJsonObject json = toJson(obj);
  52.     QJsonDocument doc(json);
  53.     return QString(doc.toJson(QJsonDocument::Compact));
  54. }
  55. QVariant EasyJsonImpl::parseObject(QJsonObject json, QMetaType typeName)
  56. {
  57.     const QMetaObject *metaInfo = typeName.metaObject();
  58.     QVariant result(typeName);
  59.     Serializable *obj = reinterpret_cast<Serializable*>(result.data());
  60.     do{
  61.         int count = metaInfo->methodCount();
  62.         for(int i=0; i< count; i++){
  63.             if (QString(metaInfo->method(i).tag()).compare("JSON_FLAG") == 0){
  64.                 QMap<QString, QString> jsonInfo = obj->invokeMethod<QMap<QString, QString>>(metaInfo, i);
  65.                 QMetaProperty fieldType = metaInfo->property(metaInfo->indexOfProperty(jsonInfo["name"].toLocal8Bit()));
  66.                 QByteArray fieldName = jsonInfo["name"].toLocal8Bit();
  67.                 if (!json.contains(jsonInfo["alias"])){
  68.                     continue;
  69.                 }
  70.                 QJsonValueRef jsonValue = json[jsonInfo["alias"]];
  71.                 // 对 Serializable 子类递归解析
  72.                 if (fieldType.metaType().id() > QMetaType::User) {
  73.                     auto valueMeta = fieldType.metaType().metaObject();
  74.                     auto classInfo = valueMeta->classInfo(valueMeta->indexOfClassInfo("base"));
  75.                     if (QString("Serializable").compare(classInfo.value()) == 0) {
  76.                         obj->setValue(fieldName,
  77.                                          parseObject(jsonValue.toObject(), fieldType.metaType()));
  78.                         continue;
  79.                     }
  80.                 }
  81.                 // 设置字段值
  82.                 obj->setValue(fieldName,
  83.                               VariantUtil::fromJsonValue(jsonValue, fieldType.metaType()));
  84.             }
  85.         }
  86.         metaInfo = metaInfo->superClass();
  87.     }while(metaInfo != nullptr);
  88.     return result;
  89. }
  90. QVariant EasyJsonImpl::parseObject(QString json, QMetaType typeName)
  91. {
  92.     if (json.isEmpty()) {
  93.         return QVariant(typeName);
  94.     }
  95.     QJsonDocument doc = QJsonDocument::fromJson(json.toLocal8Bit());
  96.     return parseObject(doc.object(), typeName);
  97. }
复制代码
variantutil 部分源码如下,详细代码请到项目 https://github.com/lsyeei/dashboard 的源码目录 /common/ 中查看 variantutil.h 文件。
  1. inline QJsonValue VariantUtil::toJsonValue(const QVariant &var)
  2. {
  3.     auto type = var.metaType();
  4.     switch (type.id()) {
  5.     case QMetaType::QPoint:
  6.         return QJsonArray{var.toPoint().x(), var.toPoint().y()};
  7.         break;
  8.     case QMetaType::QPointF:
  9.         return QJsonArray{var.toPointF().x(), var.toPointF().y()};
  10.         break;
  11.         ...
  12.         default:
  13.         if (type.flags().testFlag(QMetaType::IsEnumeration)) {
  14.             return var.toInt();
  15.         } else {
  16.             return QJsonValue::fromVariant(var);
  17.         }
  18.         break;
  19.     }
  20. }
  21. inline QVariant VariantUtil::fromJsonValue(const QJsonValue &val, QMetaType type)
  22. {
  23.     switch (type.id()) {
  24.     case QMetaType::QPoint:
  25.         return [=]{
  26.             QJsonArray array(val.toArray());
  27.             QPoint pt(array[0].toInt(), array[1].toInt());
  28.             return QVariant(pt);}();
  29.         break;
  30.     case QMetaType::QPointF:
  31.         return [=]{
  32.             QJsonArray array(val.toArray());
  33.             QPointF pt(array[0].toDouble(), array[1].toDouble());
  34.             return QVariant(pt);}();
  35.         break;
  36.         ...
  37.         default:
  38.         return val.toVariant();
  39.         break;
  40.     }
  41. }
复制代码
使用 EASYJSON

首先 .pro 文件中添加 DEFINES += EASY_JSON_DEFAULT 启用该实现类。需要序列化的数据类继承 Serializable 类,然后调用对应的方法即可EASYJSON->toJsonString(pen)。
项目 Compelling Data Designer 用于数据的可视化设计,软件采用可扩展架构,支持扩展图形插件、数据接口。项目仍在开发中,目前已设计完成基本图形、多属性配置、动画等功能。项目中还提供了 JSON 序列化数据类的实现方式。
1.png

2.gif


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册