使用C++17實(shí)現(xiàn)JSON庫設(shè)計(jì)思路示例全解
介紹
從node.js轉(zhuǎn)到c++,特別懷念在js中使用json那種暢快感。在c++中也使用過了些庫,但提供的接口使用方式,總不是習(xí)慣,很煩鎖,接口函數(shù)太多,不直觀。參考了很多庫,如:rapidjson, cJson, CJsonObject, drleq-cppjson, json11等,受cJson的數(shù)據(jù)結(jié)構(gòu)啟發(fā)很大,決定用C++手?jǐn)]一個(gè)。
最后因?yàn)閿?shù)據(jù)存儲(chǔ)需要不區(qū)分型別,又要能知道其型別,所以選擇了C++17才支持的std::variant以及std::any,最終,C++版本定格在c++17,本庫設(shè)計(jì)為單頭文件,且不依賴c++標(biāo)準(zhǔn)庫以外的任何庫。
項(xiàng)目名稱說明
本人姓名拼音第一個(gè)字母z加上josn,即得本項(xiàng)目名稱zjson,沒有其它任何意義。我將編寫一系列以z開頭的相關(guān)項(xiàng)目,命名是個(gè)很麻煩的事,因此采用了這種簡單粗暴的方式。
設(shè)計(jì)思路
簡單的接口函數(shù)、簡單的使用方法、靈活的數(shù)據(jù)結(jié)構(gòu)、盡量支持鏈?zhǔn)讲僮?。使用模板技術(shù),使用給Json對(duì)象增加值的方法只有兩個(gè),AddValueBase和AddValueJson。采用鏈表結(jié)構(gòu)(向cJSON致敬)來存儲(chǔ)Json對(duì)象,請(qǐng)看我下面的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì),表頭與后面的結(jié)點(diǎn),都用使用一致的結(jié)構(gòu),這使得在索引操作([])時(shí),可以進(jìn)行鏈?zhǔn)讲僮鳌?/p>
項(xiàng)目進(jìn)度
項(xiàng)目目前完成一半,可以新建Json對(duì)象,增加數(shù)據(jù),按key(Object類型)或索引(Array類型)提取相應(yīng)的值或子對(duì)象,生成json字符串。
已經(jīng)做過內(nèi)存泄漏測(cè)試,析構(gòu)函數(shù)能正確運(yùn)行,百萬級(jí)別生成與銷毀未見內(nèi)存明顯增長。
任務(wù)列表:
- [x] 構(gòu)造函數(shù)、復(fù)制構(gòu)造函數(shù)、析構(gòu)函數(shù)
- [x] AddValueBase(為Json對(duì)象增加值類型)、AddValueJson(為Json對(duì)象增加對(duì)象類型)
- [x] operator=、operator[]
- [x] toString(生成json字符串)
- [x] toInt、toDouble、toFalse 等值類型轉(zhuǎn)換
- [x] isError、isNull、isArray 等節(jié)點(diǎn)類型判斷
- [ ] parse, 從json字符串生成Json對(duì)象;相應(yīng)的構(gòu)造函數(shù)
- [ ] Extend Json - 擴(kuò)展對(duì)象
- [ ] Remove[All] key - 刪除數(shù)據(jù), 因?yàn)镴son對(duì)象允許重復(fù)的key
- [ ] findAll - 查找全部, 因?yàn)镴son對(duì)象允許重復(fù)的key
- [ ] std::move語義
數(shù)據(jù)結(jié)構(gòu)
Json 節(jié)點(diǎn)類型定義
(內(nèi)部使用,數(shù)據(jù)類型只在Json類內(nèi)部使用)
enum Type { Error, //錯(cuò)誤,查找無果,這是一個(gè)無效Json對(duì)象 False, //Json值類型 - false True, //Json值類型 - true Null, //Json值類型 - null Number, //Json值類型 - 數(shù)字,庫中以double類型存儲(chǔ) String, //Json值類型 - 字符串 Object, //Json類對(duì)象類型 - 這是Object嵌套,對(duì)象型中只有child需要關(guān)注 Array //Json類對(duì)象類型 - 這是Array嵌套,對(duì)象型中只有child需要關(guān)注 };
Json 節(jié)點(diǎn)定義
class Json { Json* brother; //與cJSON中的next對(duì)應(yīng),值類型才有效,指向并列的數(shù)據(jù),但有可能是值類型,也有可能是對(duì)象類型 Json* child; //孩子節(jié)點(diǎn),對(duì)象類型才有效 Type type; //節(jié)點(diǎn)類型 std::variant <int, bool, double, string> data; //節(jié)點(diǎn)數(shù)據(jù) string name; //節(jié)點(diǎn)的key }
接口說明
公開的對(duì)象類型,json只支持Object與Array兩種對(duì)象,與內(nèi)部類型對(duì)應(yīng)(公開類型)。
enum class JsonType { Object = 6, Array = 7 };
接口列表
- Json(JsonType type = JsonType::Object) //默認(rèn)構(gòu)造函數(shù),生成Object或Array類型的Json對(duì)象
- Json(const Json& origin) //復(fù)制構(gòu)造函數(shù)
- Json& operator = (const Json& origin) //賦值操作
- Json operator[](const int& index) //Json數(shù)組對(duì)象元素查詢
- Json operator[](const string& key) //Json Object 對(duì)象按key查詢
- bool AddValueJson(Json& obj) //增加子Json類對(duì)象類型, 只面向Array
- bool AddValueJson(string name, Json& obj) //增加子Json類對(duì)象類型,當(dāng)obj為Array時(shí),name會(huì)被忽略
- template<typename T> bool AddValueBase(T value) //增加值對(duì)象類型,只面向Array
- template<typename T> bool AddValueBase(string name, T value) //增加值對(duì)象類型,當(dāng)this為Array時(shí),name會(huì)被忽略
- string toString() //Json對(duì)象序列化為字符串
- bool isError() //無效Json對(duì)象判定
- bool isNull() //null值判定
- bool isObject() //Object對(duì)象判定
- bool isArray() //Array對(duì)象判定
- bool isNumber() //number值判定,Json內(nèi)使用double型別存儲(chǔ)number值
- bool isTrue() //true值判定
- bool isFalse() //false值判定
- int toInt() //值對(duì)象轉(zhuǎn)為int
- float toFloat() //值對(duì)象轉(zhuǎn)為float
- double toDouble() //值對(duì)象轉(zhuǎn)為double
- bool toBool() //值對(duì)象轉(zhuǎn)為bool
編程示例
簡單使用示例
Json ajson(JsonType::Object); //新建Object對(duì)象,輸入?yún)?shù)可以省略 std::string data = "kevin"; ajson.AddValueBase("fail", false); //增加false值對(duì)象 ajson.AddValueBase("name", data); //增加字符串值對(duì)象 ajson.AddValueBase("school-en", "the 85th."); ajson.AddValueBase("age", 10); //增加number值對(duì)象,此處為整數(shù) ajson.AddValueBase("scores", 95.98); //增加number值對(duì)象,此處為浮點(diǎn)數(shù),還支持long,long long ajson.AddValueBase("nullkey", nullptr); //增加null值對(duì)象,需要送入nullptr, NULL會(huì)被認(rèn)為是整數(shù)0 Json sub; //新建Object對(duì)象 sub.AddValueBase("math", 99); ajson.AddValueJson("subJson", sub); //為ajson增加子Json類型對(duì)象,完成嵌套需要 Json subArray(JsonType::Array); //新建Array對(duì)象,輸入?yún)?shù)不可省略 subArray.AddValueBase("I'm the first one."); //增加Array對(duì)象的字符串值子對(duì)象 subArray.AddValueBase("two", 2); //增加Array對(duì)象的number值子對(duì)象,第一個(gè)參數(shù)會(huì)被忽略 Json sub2; sub2.AddValueBase("sb2", 222); subArray.AddValueJson("subObj", sub2); //為Array對(duì)象增加Object類子對(duì)象,完成嵌套需求 ajson.AddValueJson("array", subArray); //為ajson增加Array對(duì)象,且這個(gè)Array對(duì)象本身就是一個(gè)嵌套結(jié)構(gòu) std::cout << "ajson's string is : " << ajson.toString() << std::endl; //輸出ajson對(duì)象序列化后的字符串, 結(jié)果見下方 string name = ajson["name"].toString(); //提取key為name的字符串值,結(jié)果為:kevin int oper = ajson["sb2"].toInt(); //提取嵌套深層結(jié)構(gòu)中的key為sb2的整數(shù)值,結(jié)果為:222 Json operArr = ajson["array"]; //提取key為array的數(shù)組對(duì)象 string first = ajson["array"][0].toString(); //提取key為array的數(shù)組對(duì)象的序號(hào)為0的值,結(jié)果為:I'm the first one.
ajson序列化后結(jié)果為:
{ "fail": false, "name": "kevin", "school-en": "the 85th.", "age": 10, "scores": 95.98, "nullkey": null, "subJson": { "math": 99 }, "array": [ "I'm the first one.", 2, { "sb2": 222 } ] }
詳情請(qǐng)參看demo.cpp或tests目錄下的測(cè)試用例
項(xiàng)目地址
https://gitee.com/zhoutk/zjson
https://github.com/zhoutk/zjson
運(yùn)行方法
該項(xiàng)目在vs2019, gcc7.5, clang12.0下均編譯運(yùn)行正常。
git clone https://github.com/zhoutk/zjson cd zjson cmake -Bbuild . ---windows cd build && cmake --build . ---linux & mac cd build && make run zjson or ctest
后續(xù)會(huì)有一系列相關(guān)項(xiàng)目出爐,更多關(guān)于C++17實(shí)現(xiàn)JSON庫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
關(guān)于C++ string和c類型字符數(shù)組的對(duì)比
下面小編就為大家?guī)硪黄P(guān)于C++ string和c類型字符數(shù)組的對(duì)比。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-07-07基于C語言實(shí)現(xiàn)的貪吃蛇游戲完整實(shí)例代碼
這篇文章主要介紹了基于C語言實(shí)現(xiàn)的貪吃蛇游戲完整實(shí)例代碼,對(duì)于學(xué)習(xí)游戲開發(fā)的朋友有一定的借鑒價(jià)值,需要的朋友可以參考下2014-08-08C語言深入講解之從函數(shù)棧幀角度理解return關(guān)鍵字
在C語言中,一般情況下函數(shù)的返回值是通過函數(shù)中的return語句來實(shí)現(xiàn)的,每調(diào)用一次return語句只能從函數(shù)中返回一個(gè)值,這篇文章主要給大家介紹了關(guān)于C語言從函數(shù)棧幀角度理解return關(guān)鍵字的相關(guān)資料,需要的朋友可以參考下2021-09-09C++設(shè)計(jì)模式之策略模式(Strategy)
這篇文章主要為大家詳細(xì)介紹了C++設(shè)計(jì)模式之策略模式Strategy ,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04C語言實(shí)現(xiàn)經(jīng)典掃雷小游戲完整代碼(遞歸展開?+?選擇標(biāo)記)
這篇文章主要介紹了C語言小項(xiàng)目之掃雷游戲帶遞歸展開?+?選擇標(biāo)記效果,本代碼中,我們用字符?!?來標(biāo)識(shí)雷,文中附有完整代碼,需要的朋友可以參考下2022-05-05C語言數(shù)據(jù)類型與sizeof關(guān)鍵字
這篇文章主要介紹了C語言數(shù)據(jù)類型與sizeof關(guān)鍵字,C語言的數(shù)據(jù)類型包括基本類型、構(gòu)造類型、指針類型以及空類型,下文更多相關(guān)內(nèi)容需要的小伙伴可以參考一下2022-04-04C++處理輸入字符串并轉(zhuǎn)為數(shù)組的操作
這篇文章主要介紹了C++處理輸入字符串并轉(zhuǎn)為數(shù)組的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-01-01