QT實現(xiàn)C++數(shù)據(jù)類與json轉(zhuǎn)換
QT 提供了 QJsonDocument、QJsonObject、QJsonArray、QJsonValue 等類用于 JSON 的解析和轉(zhuǎn)換。QJsonValue 支持的數(shù)據(jù)類型包括:bool、double、string、array、object、null。但是,對于 QRectF、QLineF、QColor 等類以及用戶自定義數(shù)據(jù)類,QJsonObject 就無法轉(zhuǎn)換,更無法生成可讀的字符串。此時,需要我們自己來實現(xiàn)轉(zhuǎn)換并定義轉(zhuǎn)換后的 JSON 格式。
上篇文章,借助 QT 的反射機(jī)制實現(xiàn)數(shù)據(jù)類的序列化 實現(xiàn)了數(shù)據(jù)類的序列化,簡化了數(shù)據(jù)類的編寫,同時提供了轉(zhuǎn)換為 JSON 的基礎(chǔ)。通過元對象系統(tǒng)很容易找到我們通過宏 JSONFIELD 記錄的需要序列化的字段,因為記錄序列化的方法被導(dǎo)出并標(biāo)記為 JSON_FLAG 。使用反射機(jī)制就可以找到所有記錄序列化字段的方法,獲取字段名后通過 getValue()、setValue() 即可獲取或設(shè)置字段值。
// 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. 將數(shù)據(jù)類轉(zhuǎn)換為 QJsonObject 對象;2. 將數(shù)據(jù)類轉(zhuǎn)換為字符串;3. 將字符串解析為指定的數(shù)據(jù)類;4. 將 QJsonObject 轉(zhuǎn)換為指定的數(shù)據(jù)類;
系統(tǒng)允許多個接口實現(xiàn)類,但是全局只允許有一個實例,用于整個工程的 JSON 轉(zhuǎn)換。所以聲明了一個全局的 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 的實現(xiàn)類
實現(xiàn)類直接繼承 EasyJson 類,完成接口代碼即可。QT 中數(shù)據(jù)類轉(zhuǎn)換為 JSON 的難點在于 QRectF、QSizeF 等類的轉(zhuǎn)換,以及 Serializable 作為數(shù)據(jù)類字段時的轉(zhuǎn)換。為了便于 QT 內(nèi)部封裝類的解析,需要將解析方法單獨封裝為一個工具類,這樣便于后期添加和修改。工具類的實現(xiàn)見 variantutil.h 文件。
// easyjsonimpl.h #include "easyjson.h" class EasyJsonImpl: public EasyJson { public: EasyJsonImpl(); // EasyJson interface private: 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 實現(xiàn)類,EASYJSON 對象的創(chuàng)建與否需要通過指定的宏來判斷一下。如 EasyJsonImpl 源碼中規(guī)定只有定義了 EASY_JSON_DEFAULT 才會實例化 EasyJsonImpl。
這樣在 .pro 文件中添加 DEFINES += EASY_JSON_DEFAULT
即可啟用該實現(xiàn)類。如果有不同的實現(xiàn)類,定義不同的宏即可。
// 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_DEFAULT EasyJson *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 子類遞歸轉(zhuǎn)換 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; } } // 轉(zhuǎn)為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; } } // 設(shè)置字段值 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 部分源碼如下,詳細(xì)代碼請到項目 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
啟用該實現(xiàn)類。需要序列化的數(shù)據(jù)類繼承 Serializable 類,然后調(diào)用對應(yīng)的方法即可EASYJSON->toJsonString(pen)
。
項目 Compelling Data Designer 用于數(shù)據(jù)的可視化設(shè)計,軟件采用可擴(kuò)展架構(gòu),支持?jǐn)U展圖形插件、數(shù)據(jù)接口。項目仍在開發(fā)中,目前已設(shè)計完成基本圖形、多屬性配置、動畫等功能。項目中還提供了 JSON 序列化數(shù)據(jù)類的實現(xiàn)方式。
到此這篇關(guān)于QT實現(xiàn)C++數(shù)據(jù)類與json轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)QT C++數(shù)據(jù)類轉(zhuǎn)json內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言數(shù)組越界引發(fā)的死循環(huán)問題解決
本文主要介紹了C語言數(shù)組越界引發(fā)的死循環(huán)問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08使用pybind11封裝C++結(jié)構(gòu)體作為參數(shù)的函數(shù)實現(xiàn)步驟
這篇文章主要介紹了用pybind11封裝C++結(jié)構(gòu)體作為參數(shù)的函數(shù)實現(xiàn)步驟,本文分步驟通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02C語言數(shù)據(jù)結(jié)構(gòu)順序表的進(jìn)階講解
程序中經(jīng)常需要將一組數(shù)據(jù)元素作為整體管理和使用,需要創(chuàng)建這種元素組,用變量記錄它們,傳進(jìn)傳出函數(shù)等。一組數(shù)據(jù)中包含的元素個數(shù)可能發(fā)生變化,順序表則是將元素順序地存放在一塊連續(xù)的存儲區(qū)里,元素間的順序關(guān)系由它們的存儲順序自然表示2022-04-04