C++類結(jié)構(gòu)體與json相互轉(zhuǎn)換
1. 背景與需求
之前寫C#的時(shí)候,解析json
字符串一般使用的是開源的類庫Newtonsoft.Json
,方法十分簡潔,比如:
class Project { public string Input { get; set; } public string Output { get; set; } } JavaScriptSerializer serializer = new JavaScriptSerializer(); Project test = serializer.Deserialize<Project>(@"{"Input":"1","Output":"2"}");
一行代碼就能將json字符串轉(zhuǎn)為相應(yīng)的類對(duì)象。
最近寫C++需要處理一下json
數(shù)據(jù),于是上github
找了很多很強(qiáng)大的開源庫,像jsoncpp
、rapidjson
、json
,基本上都滿足了開發(fā)需求,但想要做成像寫C#那樣子就要做二次開發(fā)。于是有了自己寫一個(gè)簡單的json轉(zhuǎn)類 | 結(jié)構(gòu)體的工具的想法(開源地址)。
需求如下:
- 只有頭文件,方便使用
- 最多三行代碼解決轉(zhuǎn)換
- 支持類|結(jié)構(gòu)體 與
json
的相互轉(zhuǎn)換 - 支持多種基本數(shù)據(jù)類型,如
int
、float
、string
、bool
等 - 支持STL基本類型,如
vector
、list
、map<string
,T>等 - 支持嵌套關(guān)系
- 支持成員重命名,比方說
json
中的關(guān)鍵字是name
,成員命名可寫成Name或其他。
2. 最終使用的樣例代碼
class Student { public: string Name; int Age; AIGC_JSON_HELPER(Name, Age)//成員注冊 AIGC_JSON_HELPER_RENAME("name","age")//成員重命名,不需要可以刪除這條 }; int main() { //json轉(zhuǎn)類對(duì)象 Student person; JsonHelper::JsonToObject(person, R"({"name":"XiaoMing", "age":15})"); //類對(duì)象轉(zhuǎn)json string jsonStr; JsonHelper::ObjectToJson(person, jsonStr); return 0; }
3. 實(shí)現(xiàn)方法
因?yàn)閯偤?code>rapidjson只需要頭文件就可以使用,所以選擇了rapidjson
作為基礎(chǔ)庫,進(jìn)行二次開發(fā)。
3.1 基礎(chǔ)類型的轉(zhuǎn)換
作為最底層的接口,只需要進(jìn)行一個(gè)賦值的操作即可,后續(xù)如果想要增加一些其他類型支持,添加起來也比較方便。
static bool JsonToObject(int &obj, rapidjson::Value &jsonValue) { if (jsonValue.IsNull() || !jsonValue.IsInt()) return false; obj = jsonValue.GetInt(); return true; } static bool JsonToObject(unsigned int &obj, rapidjson::Value &jsonValue) { if (jsonValue.IsNull() || !jsonValue.IsUint()) return false; obj = jsonValue.GetUint(); return true; } static bool JsonToObject(int64_t &obj, rapidjson::Value &jsonValue) { if (jsonValue.IsNull() || !jsonValue.IsInt64()) return false; obj = jsonValue.GetInt64(); return true; } //其他類型... ...
3.2 類成員注冊
這里使用宏定義方式 + 可變參數(shù)模板的方式來實(shí)現(xiàn),即可依次對(duì)注冊的成員進(jìn)行賦值
template <typename TYPE, typename... TYPES> static bool WriteMembers(std::vector<std::string> &names, int index, rapidjson::Value &jsonValue, TYPE &arg, TYPES &... args) { if (!WriteMembers(names, index, jsonValue, arg)) return false; return WriteMembers(names, ++index, jsonValue, args...); } template <typename TYPE> static bool WriteMembers(std::vector<std::string> &names, int index, rapidjson::Value &jsonValue, TYPE &arg) { const char *key = names[index].c_str(); if (!jsonValue.HasMember(key)) return true; if (!JsonToObject(arg, jsonValue[key])) return false; return true; } #define AIGC_JSON_HELPER(...) \ bool AIGC_CONVER_JSON_TO_OBJECT(rapidjson::Value &jsonValue, std::vector<std::string> &names) \ { \ if (names.size() <= 0) \ names = aigc::JsonHelper::GetMembersNames(#__VA_ARGS__); \ return aigc::JsonHelper::WriteMembers(names, 0, jsonValue, __VA_ARGS__); \ }
3.3 自定義類的轉(zhuǎn)換
自定義類由于并不清楚外界使用時(shí),是否有按規(guī)定添加好成員注冊接口,所以這里采用enable_if
的方式來嘗試調(diào)用,編譯的時(shí)候也就不會(huì)報(bào)錯(cuò)。
template <bool, class TYPE = void> struct enable_if { }; template <class TYPE> struct enable_if<true, TYPE> { typedef TYPE type; }; template <typename T> struct HasConverFunction { template <typename TT> static char func(decltype(&TT::AIGC_CONVER_JSON_TO_OBJECT)); template <typename TT> static int func(...); const static bool has = (sizeof(func<T>(NULL)) == sizeof(char)); }; template <typename T, typename enable_if<HasConverFunction<T>::has, int>::type = 0> static inline bool JsonToObject(T &obj, rapidjson::Value &jsonValue) { std::vector<std::string> names = LoadRenameArray(obj); return obj.AIGC_CONVER_JSON_TO_OBJECT(jsonValue, names); } template <typename T, typename enable_if<!HasConverFunction<T>::has, int>::type = 0> static inline bool JsonToObject(T &obj, rapidjson::Value &jsonValue) { return false; }
3.4 外部調(diào)用接口
/** * @brief conver json string to class | struct * @param obj : class or struct * @param jsonStr : json string */ template <typename T> static inline bool JsonToObject(T &obj, const std::string &jsonStr) { rapidjson::Document root; root.Parse(jsonStr.c_str()); if (root.IsNull()) return false; return JsonToObject(obj, root); }
最核心的部分也就上面的幾個(gè)模塊,其他的都是一些瑣碎的增加類型支持等操作。
到此這篇關(guān)于C++類結(jié)構(gòu)體與json相互轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)C++類結(jié)構(gòu)體與json相互轉(zhuǎn)換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++中結(jié)構(gòu)體的類型定義和初始化以及變量引用
- C++動(dòng)態(tài)分配和撤銷內(nèi)存以及結(jié)構(gòu)體類型作為函數(shù)參數(shù)
- C++中聲明類的class與聲明結(jié)構(gòu)體的struct關(guān)鍵字詳解
- C++結(jié)構(gòu)體struct和類class區(qū)別詳解
- C++結(jié)構(gòu)體與類指針知識(shí)點(diǎn)總結(jié)
- C++關(guān)于類結(jié)構(gòu)體大小和構(gòu)造順序,析構(gòu)順序的測試詳解
- C++結(jié)構(gòu)體與類的區(qū)別詳情
- C++?中的?JSON?序列化和反序列化及結(jié)構(gòu)體與枚舉類型的處理方法
- C++ 中類(class)和結(jié)構(gòu)體(struct)的區(qū)別
相關(guān)文章
關(guān)于C++中的static關(guān)鍵字的總結(jié)
C++的static有兩種用法:面向過程程序設(shè)計(jì)中的static和面向?qū)ο蟪绦蛟O(shè)計(jì)中的static。前者應(yīng)用于普通變量和函數(shù),不涉及類;后者主要說明static在類中的作用2013-09-09C++實(shí)現(xiàn)分水嶺算法(Watershed Algorithm)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)分水嶺算法Watershed Algorithm,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一 下2018-01-01Qt 信號(hào)自定義槽函數(shù)的實(shí)現(xiàn)
Qt中實(shí)現(xiàn)自定義信號(hào)與槽函數(shù),信號(hào)用于發(fā)送并觸發(fā)槽函數(shù),槽函數(shù)則是具體的功能實(shí)現(xiàn),本文就詳細(xì)的介紹一下如何使用,感興趣的可以了解一下2021-11-11C語言實(shí)現(xiàn)個(gè)人財(cái)務(wù)管理
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)個(gè)人財(cái)務(wù)管理,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11從匯編看c++的默認(rèn)析構(gòu)函數(shù)的使用詳解
本篇文章是對(duì)c++中默認(rèn)析構(gòu)函數(shù)的使用進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下2013-05-05