写点什么

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

  • 2025-04-23
    福建
  • 本文字数:4372 字

    阅读完需:约 14 分钟

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() 即可获取或设置字段值。


// serializable.h#define JSONFIELD(field, alias, ...) \using __type_##field = decltype(field) ;\Q_PROPERTY(__type_##field field READ get##alias WRITE set##alias) \    public: \    Q_INVOKABLE JSON_FLAG inline QMap<QString, QString> __get##alias##Info__(){ \        QMap<QString, QString> info; \        info["name"] = #field; \        info["alias"] = #alias; \        info["args"] = QString(#__VA_ARGS__); \        return info; \    } \    inline __type_##field get##alias() const { return field; } \    inline void set##alias(const __type_##field &value) { \            field = value; \    }
复制代码


定义通用的 JSON 接口


JSON 接口主要定义 4 个功能接口:1. 将数据类转换为 QJsonObject 对象;2. 将数据类转换为字符串;3. 将字符串解析为指定的数据类;4. 将 QJsonObject 转换为指定的数据类;系统允许多个接口实现类,但是全局只允许有一个实例,用于整个工程的 JSON 转换。所以声明了一个全局的 EasyJson 对象 EASYJSON。


#include "serializable.h"
#include <QJsonObject>
class EasyJson{public: EasyJson(){} ~EasyJson(){} virtual QJsonObject toJson(const Serializable &obj) = 0; virtual QString toJsonString(const Serializable &obj) = 0; virtual QVariant parseObject(QJsonObject json, QMetaType typeName) = 0; virtual QVariant parseObject(QString json, QMetaType typeName) = 0;};
extern EasyJson *EASYJSON;
复制代码


EasyJson 的实现类


实现类直接继承 EasyJson 类,完成接口代码即可。QT 中数据类转换为 JSON 的难点在于 QRectF、QSizeF 等类的转换,以及 Serializable 作为数据类字段时的转换。为了便于 QT 内部封装类的解析,需要将解析方法单独封装为一个工具类,这样便于后期添加和修改。工具类的实现见 variantutil.h 文件。


// easyjsonimpl.h#include "easyjson.h"
class EasyJsonImpl: public EasyJson{public: EasyJsonImpl();
// EasyJson interfaceprivate: QJsonObject toJson(const Serializable &obj) override; QString toJsonString(const Serializable &obj) override; QVariant parseObject(QJsonObject json, QMetaType typeName) override; QVariant parseObject(QString json, QMetaType typeName) override;};
复制代码


为了便于切换不同的 JSON 实现类,EASYJSON 对象的创建与否需要通过指定的宏来判断一下。如 EasyJsonImpl 源码中规定只有定义了 EASY_JSON_DEFAULT 才会实例化 EasyJsonImpl。这样在 .pro 文件中添加 DEFINES += EASY_JSON_DEFAULT 即可启用该实现类。如果有不同的实现类,定义不同的宏即可。


// easyjsonimpl.cpp#include "easyjsonimpl.h"#include "variantutil.h"
#include <QObject>#include <QMetaObject>#include <QMetaProperty>#include <QColor>#include <QJsonArray>#include <QLineF>#include <QPointF>#include <QRectF>#include <QSizeF>#include <QJsonDocument>
#ifdef EASY_JSON_DEFAULTEasyJson *EASYJSON = new EasyJsonImpl();#endif
EasyJsonImpl::EasyJsonImpl() {}
QJsonObject EasyJsonImpl::toJson(const Serializable &obj){ QJsonObject json; Serializable *objPtr = const_cast<Serializable*>(&obj);
const QMetaObject *metaInfo = obj.getMetaInfo();//obj.metaObject(); do{ int count = metaInfo->methodCount(); for(int i=0; i< count; i++){ if (QString(metaInfo->method(i).tag()).compare("JSON_FLAG") == 0){ QMap<QString, QString> jsonInfo; jsonInfo = objPtr->invokeMethod<QMap<QString, QString>>(metaInfo, i); QString alias = jsonInfo["alias"]; QVariant value = objPtr->getValue(jsonInfo["name"]); QMetaType type = value.metaType(); // 对 Serializable 子类递归转换 if (type.id() > QMetaType::User) { auto valueMeta = type.metaObject(); auto classInfo = valueMeta->classInfo(valueMeta->indexOfClassInfo("base")); if (QString("Serializable").compare(classInfo.value()) == 0) { json.insert(alias, toJson(*reinterpret_cast<const Serializable*>(value.constData()))); continue; } } // 转为json对象 json.insert(alias, VariantUtil::toJsonValue(value)); } }
metaInfo = metaInfo->superClass(); }while(metaInfo != nullptr); return json;}
QString EasyJsonImpl::toJsonString(const Serializable &obj){ QJsonObject json = toJson(obj); QJsonDocument doc(json); return QString(doc.toJson(QJsonDocument::Compact));}
QVariant EasyJsonImpl::parseObject(QJsonObject json, QMetaType typeName){ const QMetaObject *metaInfo = typeName.metaObject(); QVariant result(typeName); Serializable *obj = reinterpret_cast<Serializable*>(result.data()); do{ int count = metaInfo->methodCount(); for(int i=0; i< count; i++){ if (QString(metaInfo->method(i).tag()).compare("JSON_FLAG") == 0){ QMap<QString, QString> jsonInfo = obj->invokeMethod<QMap<QString, QString>>(metaInfo, i);
QMetaProperty fieldType = metaInfo->property(metaInfo->indexOfProperty(jsonInfo["name"].toLocal8Bit())); QByteArray fieldName = jsonInfo["name"].toLocal8Bit(); if (!json.contains(jsonInfo["alias"])){ continue; } QJsonValueRef jsonValue = json[jsonInfo["alias"]]; // 对 Serializable 子类递归解析 if (fieldType.metaType().id() > QMetaType::User) { auto valueMeta = fieldType.metaType().metaObject(); auto classInfo = valueMeta->classInfo(valueMeta->indexOfClassInfo("base")); if (QString("Serializable").compare(classInfo.value()) == 0) { obj->setValue(fieldName, parseObject(jsonValue.toObject(), fieldType.metaType())); continue; } } // 设置字段值 obj->setValue(fieldName, VariantUtil::fromJsonValue(jsonValue, fieldType.metaType())); } } metaInfo = metaInfo->superClass(); }while(metaInfo != nullptr); return result;}
QVariant EasyJsonImpl::parseObject(QString json, QMetaType typeName){ if (json.isEmpty()) { return QVariant(typeName); } QJsonDocument doc = QJsonDocument::fromJson(json.toLocal8Bit()); return parseObject(doc.object(), typeName);}
复制代码


variantutil 部分源码如下,详细代码请到项目 https://github.com/lsyeei/dashboard 的源码目录 /common/ 中查看 variantutil.h 文件。


inline QJsonValue VariantUtil::toJsonValue(const QVariant &var){    auto type = var.metaType();    switch (type.id()) {    case QMetaType::QPoint:        return QJsonArray{var.toPoint().x(), var.toPoint().y()};        break;    case QMetaType::QPointF:        return QJsonArray{var.toPointF().x(), var.toPointF().y()};        break;	...	default:        if (type.flags().testFlag(QMetaType::IsEnumeration)) {            return var.toInt();        } else {            return QJsonValue::fromVariant(var);        }        break;    }}
inline QVariant VariantUtil::fromJsonValue(const QJsonValue &val, QMetaType type){ switch (type.id()) { case QMetaType::QPoint: return [=]{ QJsonArray array(val.toArray()); QPoint pt(array[0].toInt(), array[1].toInt()); return QVariant(pt);}(); break; case QMetaType::QPointF: return [=]{ QJsonArray array(val.toArray()); QPointF pt(array[0].toDouble(), array[1].toDouble()); return QVariant(pt);}(); break; ... default: return val.toVariant(); break; }}
复制代码


使用 EASYJSON


首先 .pro 文件中添加 DEFINES += EASY_JSON_DEFAULT 启用该实现类。需要序列化的数据类继承 Serializable 类,然后调用对应的方法即可EASYJSON->toJsonString(pen)


项目 Compelling Data Designer 用于数据的可视化设计,软件采用可扩展架构,支持扩展图形插件、数据接口。项目仍在开发中,目前已设计完成基本图形、多属性配置、动画等功能。项目中还提供了 JSON 序列化数据类的实现方式。



文章转载自:永不停转

原文链接:https://www.cnblogs.com/ITnoteforlsy/p/18840486

体验地址:http://www.jnpfsoft.com/?from=001YH


用户头像

还未添加个人签名 2025-04-01 加入

还未添加个人简介

评论

发布
暂无评论
QT 实现 C++ 数据类与 json 的转换_c++_电子尖叫食人鱼_InfoQ写作社区