C++自動序列化和反序列化在實際軟件開發(fā)中的應用(二)
“上一篇文章介紹了自動進行序列化和反序列化的方法,其中也指出了其中存在的問題以及不足,今天這篇文章來詳細說下如何處理
背景
目前使用自動生成屬性的方法支持的數(shù)據(jù)類型有限,并不是所有的數(shù)據(jù)類型有支持,不支持的類型生成的鍵值對的值會是 null
,肯定不是我們想要的,因此需要對這些類型進行自定義擴展。
目前不支持的類型主要有以下幾種場景:
QRect
QSize
QPoint
QList<T>,QVector<T>,比如: QList<int>,QList<float>,QList<double>……
QPolygon, QPolygonF
QLine,QLineF
QMap, QSet,QHash……
std 標準庫數(shù)據(jù)類型
其它自定義類型
原因
為什么上述類型不支持呢?有兩個原因:
第一,上述這些類型無法反向解析,無法進行有效的區(qū)分,比如你想把一個QSize
保存成什么樣子?
數(shù)組形式:[10,10]
字符串:"QSize(10,10)"
不管哪種方式,都不是通用的,數(shù)組格式在反序列化時無法還原,除非手動進行,字符串方式有冗余字段無法滿足三方使用。
第二,其實不怪 Qt
,JSON
的鍵值就支持這些類型,[侯捷] 曾說過「源碼面前了無秘密」,我們再順便看下Qt
源碼。
某個字段的值是這樣獲取的,返回的是一個 QVariant 類型,這個類型本身可以支持多種數(shù)據(jù)類型
QVariant?v?=?object->property(proName);
將獲取的鍵值插入到 QJsonObject
當中:
jsObj.insert(proName,?QJsonValue::fromVariant(v));
問題就出在這里,JSON
對象的值需要一個 QJsonValue
類型,但是 QJsonValue
僅僅支持常見的基本數(shù)據(jù)類型
????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);
基于上述兩個原因,我們能夠做的只能是把其它類型轉(zhuǎn)換成標準JSON
支持的類型。
方案
JSON作為通用的數(shù)據(jù)格式,一般普遍做法需要針對特殊類型字段單獨處理,再反序列化時也需要單獨處理,下面以QPoint、QSize
兩種類型為例詳細展開說下:
????Q_PROPERTY(QSize?testSize?READ?testSize?WRITE?setTestSize)
????Q_PROPERTY(QPoint?testPoint?READ?testPoint?WRITE?setTestPoint)
在進行序列化時,判斷該屬性類型,然后分別進行處理:
????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 是類似的,因此需要寫一個模板來統(tǒng)一處理:
template?<class?Size>
static?inline?QVariant?serializeSize(const?Size?&size)
{
????return?QVariant(QJsonArray{size.width(),?size.height()});
}
擴展
一般我們能想到的是分別實現(xiàn)對QSize
和QSizeF
進行處理,這個時候你會寫兩個函數(shù)來處理,進階后你會寫一個模板來處理,那么再次進階下,怎么處理呢?
來看一個更高級的用法(語法糖,C++17才有的)
QJsonValue?serializeSize(const?std::variant<QSize,?QSizeF>?&size)?const
{
?return
??std::visit([](const?auto?&s)?->?QJsonArray?{
???return?{s.width(),?s.height()};
??},?size);
}
“上述代碼可以參考開源項目: https://github.com/Skycoder42/QtJsonSerializer
總結(jié)
上述提供了一種方案和思路,按照這個思路繼續(xù)擴展其它數(shù)據(jù)類型即可,如果感興趣可以直接看這個開源項目:https://github.com/Skycoder42/QtJsonSerializer, 不過需要 Qt5.12
及以上版本才支持哦
如果想自己動手實現(xiàn),順便深入學習下 Qt
元對象系統(tǒng),那么可以一起參與進來,從零實現(xiàn)一個簡易版本的序列化庫: https://github.com/kevinlq/KSerialize.
授人以魚不如授人以漁, 方案和思路有了,關鍵還是要多動手寫起來,如果有問題隨時留言交流。