QT實(shí)現(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。但是,對(duì)于 QRectF、QLineF、QColor 等類以及用戶自定義數(shù)據(jù)類,QJsonObject 就無法轉(zhuǎn)換,更無法生成可讀的字符串。此時(shí),需要我們自己來實(shí)現(xiàn)轉(zhuǎn)換并定義轉(zhuǎn)換后的 JSON 格式。
上篇文章,借助 QT 的反射機(jī)制實(shí)現(xiàn)數(shù)據(jù)類的序列化 實(shí)現(xiàn)了數(shù)據(jù)類的序列化,簡(jiǎn)化了數(shù)據(jù)類的編寫,同時(shí)提供了轉(zhuǎn)換為 JSON 的基礎(chǔ)。通過元對(duì)象系統(tǒng)很容易找到我們通過宏 JSONFIELD 記錄的需要序列化的字段,因?yàn)橛涗浶蛄谢姆椒ū粚?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 個(gè)功能接口:1. 將數(shù)據(jù)類轉(zhuǎn)換為 QJsonObject 對(duì)象;2. 將數(shù)據(jù)類轉(zhuǎn)換為字符串;3. 將字符串解析為指定的數(shù)據(jù)類;4. 將 QJsonObject 轉(zhuǎn)換為指定的數(shù)據(jù)類;
系統(tǒng)允許多個(gè)接口實(shí)現(xiàn)類,但是全局只允許有一個(gè)實(shí)例,用于整個(gè)工程的 JSON 轉(zhuǎn)換。所以聲明了一個(gè)全局的 EasyJson 對(duì)象 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 的實(shí)現(xiàn)類
實(shí)現(xiàn)類直接繼承 EasyJson 類,完成接口代碼即可。QT 中數(shù)據(jù)類轉(zhuǎn)換為 JSON 的難點(diǎn)在于 QRectF、QSizeF 等類的轉(zhuǎn)換,以及 Serializable 作為數(shù)據(jù)類字段時(shí)的轉(zhuǎn)換。為了便于 QT 內(nèi)部封裝類的解析,需要將解析方法單獨(dú)封裝為一個(gè)工具類,這樣便于后期添加和修改。工具類的實(shí)現(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 實(shí)現(xiàn)類,EASYJSON 對(duì)象的創(chuàng)建與否需要通過指定的宏來判斷一下。如 EasyJsonImpl 源碼中規(guī)定只有定義了 EASY_JSON_DEFAULT 才會(huì)實(shí)例化 EasyJsonImpl。
這樣在 .pro 文件中添加 DEFINES += EASY_JSON_DEFAULT 即可啟用該實(shí)現(xiàn)類。如果有不同的實(shí)現(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();
// 對(duì) 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對(duì)象
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"]];
// 對(duì) 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ì)代碼請(qǐng)到項(xiàng)目 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 啟用該實(shí)現(xiàn)類。需要序列化的數(shù)據(jù)類繼承 Serializable 類,然后調(diào)用對(duì)應(yīng)的方法即可EASYJSON->toJsonString(pen)。
項(xiàng)目 Compelling Data Designer 用于數(shù)據(jù)的可視化設(shè)計(jì),軟件采用可擴(kuò)展架構(gòu),支持?jǐn)U展圖形插件、數(shù)據(jù)接口。項(xiàng)目仍在開發(fā)中,目前已設(shè)計(jì)完成基本圖形、多屬性配置、動(dòng)畫等功能。項(xiàng)目中還提供了 JSON 序列化數(shù)據(jù)類的實(shí)現(xiàn)方式。


到此這篇關(guān)于QT實(shí)現(xiàn)C++數(shù)據(jù)類與json轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)QT C++數(shù)據(jù)類轉(zhuǎn)json內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言數(shù)組越界引發(fā)的死循環(huán)問題解決
本文主要介紹了C語(yǔ)言數(shù)組越界引發(fā)的死循環(huán)問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
C語(yǔ)言詳細(xì)講解qsort函數(shù)的使用
排序方法有很多種:選擇排序,冒泡排序,歸并排序,快速排序等??疵侄贾揽焖倥判蚴悄壳肮J(rèn)的一種比較好的排序算法。因?yàn)樗俣群芸?,所以系統(tǒng)也在庫(kù)里實(shí)現(xiàn)這個(gè)算法,便于我們的使用。這就是qsort函數(shù)2022-04-04
C語(yǔ)言字符串函數(shù)介紹與模擬實(shí)現(xiàn)詳解
字符串函數(shù)(String?processing?function)也叫字符串處理函數(shù),指的是編程語(yǔ)言中用來進(jìn)行字符串處理的函數(shù),如C,pascal,Visual以及LotusScript中進(jìn)行字符串拷貝,計(jì)算長(zhǎng)度,字符查找等的函數(shù)2022-02-02
使用pybind11封裝C++結(jié)構(gòu)體作為參數(shù)的函數(shù)實(shí)現(xiàn)步驟
這篇文章主要介紹了用pybind11封裝C++結(jié)構(gòu)體作為參數(shù)的函數(shù)實(shí)現(xiàn)步驟,本文分步驟通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-02-02
C語(yǔ)言數(shù)據(jù)結(jié)構(gòu)順序表的進(jìn)階講解
程序中經(jīng)常需要將一組數(shù)據(jù)元素作為整體管理和使用,需要?jiǎng)?chuàng)建這種元素組,用變量記錄它們,傳進(jìn)傳出函數(shù)等。一組數(shù)據(jù)中包含的元素個(gè)數(shù)可能發(fā)生變化,順序表則是將元素順序地存放在一塊連續(xù)的存儲(chǔ)區(qū)里,元素間的順序關(guān)系由它們的存儲(chǔ)順序自然表示2022-04-04
OpenCV實(shí)現(xiàn)輪廓的發(fā)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了OpenCV如何實(shí)現(xiàn)輪廓的發(fā)現(xiàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05

