上一篇文章介紹了自動(dòng)進(jìn)行序列化和反序列化的方法沈条,其中也指出了其中存在的問(wèn)題以及不足综苔,今天這篇文章來(lái)詳細(xì)說(shuō)下如何處理
背景
目前使用自動(dòng)生成屬性的方法支持的數(shù)據(jù)類(lèi)型有限,并不是所有的數(shù)據(jù)類(lèi)型有支持,不支持的類(lèi)型生成的鍵值對(duì)的值會(huì)是 null
逼泣,肯定不是我們想要的,因此需要對(duì)這些類(lèi)型進(jìn)行自定義擴(kuò)展舟舒。
目前不支持的類(lèi)型主要有以下幾種場(chǎng)景:
- QRect
- QSize
- QPoint
- QList<T>拉庶,QVector<T>,比如: QList<int>秃励,QList<float>氏仗,QList<double>……
- QPolygon, QPolygonF
- QLine,QLineF
- QMap, QSet,QHash……
- std 標(biāo)準(zhǔn)庫(kù)數(shù)據(jù)類(lèi)型
- 其它自定義類(lèi)型
原因
為什么上述類(lèi)型不支持呢莺治?有兩個(gè)原因:
第一廓鞠,上述這些類(lèi)型無(wú)法反向解析,無(wú)法進(jìn)行有效的區(qū)分谣旁,比如你想把一個(gè)QSize
保存成什么樣子床佳?
- 數(shù)組形式:[10,10]
- 字符串:"QSize(10,10)"
不管哪種方式,都不是通用的榄审,數(shù)組格式在反序列化時(shí)無(wú)法還原砌们,除非手動(dòng)進(jìn)行,字符串方式有冗余字段無(wú)法滿(mǎn)足三方使用搁进。
第二浪感,其實(shí)不怪 Qt
,JSON
的鍵值就支持這些類(lèi)型,[侯捷] 曾說(shuō)過(guò)「源碼面前了無(wú)秘密」,我們?cè)夙槺憧聪?code>Qt源碼饼问。
某個(gè)字段的值是這樣獲取的影兽,返回的是一個(gè) QVariant 類(lèi)型,這個(gè)類(lèi)型本身可以支持多種數(shù)據(jù)類(lèi)型
QVariant v = object->property(proName);
將獲取的鍵值插入到 QJsonObject
當(dāng)中:
jsObj.insert(proName, QJsonValue::fromVariant(v));
問(wèn)題就出在這里莱革,JSON
對(duì)象的值需要一個(gè) QJsonValue
類(lèi)型峻堰,但是 QJsonValue
僅僅支持常見(jiàn)的基本數(shù)據(jù)類(lèi)型
QJsonValue(Type = Null);
QJsonValue(bool b);
QJsonValue(double n);
QJsonValue(int n);
QJsonValue(qint64 v);
QJsonValue(const QString &s);
QJsonValue(QLatin1String s);
QJsonValue(const QJsonArray &a);
QJsonValue(const QJsonObject &o);
基于上述兩個(gè)原因讹开,我們能夠做的只能是把其它類(lèi)型轉(zhuǎn)換成標(biāo)準(zhǔn)JSON
支持的類(lèi)型。
方案
JSON作為通用的數(shù)據(jù)格式捐名,一般普遍做法需要針對(duì)特殊類(lèi)型字段單獨(dú)處理旦万,再反序列化時(shí)也需要單獨(dú)處理,下面以QPoint镶蹋、QSize
兩種類(lèi)型為例詳細(xì)展開(kāi)說(shuō)下:
Q_PROPERTY(QSize testSize READ testSize WRITE setTestSize)
Q_PROPERTY(QPoint testPoint READ testPoint WRITE setTestPoint)
在進(jìn)行序列化時(shí)成艘,判斷該屬性類(lèi)型,然后分別進(jìn)行處理:
switch (propertyType)
{
case QMetaType::QSize: return serializeSize(value.toSize());
case QMetaType::QSizeF: return serializeSize(value.toSizeF());
default: throw KException{ QByteArray("Invalid type id ") +
QByteArray(QMetaType::typeName(propertyType))};
}
QSize和QSizeF 是類(lèi)似的贺归,因此需要寫(xiě)一個(gè)模板來(lái)統(tǒng)一處理:
template <class Size>
static inline QVariant serializeSize(const Size &size)
{
return QVariant(QJsonArray{size.width(), size.height()});
}
擴(kuò)展
一般我們能想到的是分別實(shí)現(xiàn)對(duì)QSize
和QSizeF
進(jìn)行處理淆两,這個(gè)時(shí)候你會(huì)寫(xiě)兩個(gè)函數(shù)來(lái)處理,進(jìn)階后你會(huì)寫(xiě)一個(gè)模板來(lái)處理拂酣,那么再次進(jìn)階下琼腔,怎么處理呢?
來(lái)看一個(gè)更高級(jí)的用法(語(yǔ)法糖踱葛,C++17才有的)
QJsonValue serializeSize(const std::variant<QSize, QSizeF> &size) const
{
return
std::visit([](const auto &s) -> QJsonArray {
return {s.width(), s.height()};
}, size);
}
上述代碼可以參考開(kāi)源項(xiàng)目: https://github.com/Skycoder42/QtJsonSerializer
總結(jié)
上述提供了一種方案和思路丹莲,按照這個(gè)思路繼續(xù)擴(kuò)展其它數(shù)據(jù)類(lèi)型即可,如果感興趣可以直接看這個(gè)開(kāi)源項(xiàng)目:https://github.com/Skycoder42/QtJsonSerializer尸诽, 不過(guò)需要 Qt5.12
及以上版本才支持哦
如果想自己動(dòng)手實(shí)現(xiàn)甥材,順便深入學(xué)習(xí)下 Qt
元對(duì)象系統(tǒng),那么可以一起參與進(jìn)來(lái)性含,從零實(shí)現(xiàn)一個(gè)簡(jiǎn)易版本的序列化庫(kù): https://github.com/kevinlq/KSerialize.
授人以魚(yú)不如授人以漁洲赵, 方案和思路有了,關(guān)鍵還是要多動(dòng)手寫(xiě)起來(lái),如果有問(wèn)題隨時(shí)留言交流商蕴。