JSON數(shù)據(jù)轉(zhuǎn)C++結構體

JSON數(shù)據(jù)自動生成C++結構體

生成的c++結構體基于nlohmann/json進行解析斗塘,實現(xiàn)了類似JavaBean和C#中JsonConvert.SerializeObject的效果,將c++結構體與Json數(shù)據(jù)結構進行了映射宣赔,使得json解析成c++對象這一過程對上層屏蔽,可以實現(xiàn)快速開發(fā)。

背景

在編寫服務端程序時睛琳,除了和系統(tǒng)交互、業(yè)務邏輯的內(nèi)部實現(xiàn)踏烙,最主要的一部分就是和客戶端打交道。現(xiàn)在web服務器開發(fā)历等,最流行的數(shù)據(jù)傳輸格式基本是Json讨惩、Xml、Protobuf寒屯,其中Json格式由于其和javascript語言對象模型的兼容性最好荐捻,成為b/s模型下最常用的數(shù)據(jù)傳輸格式。

在高級語言如Java寡夹、C#中处面,有一些內(nèi)置的庫實現(xiàn)了語言對象模型和Json數(shù)據(jù)間的自動轉(zhuǎn)換,這一點著實讓cpper羨慕不已菩掏。雖然c++也有一些成熟的開源解析庫如nlohmann/json魂角、RapidJsonJsoncpp等智绸,讓解析Json已經(jīng)變得相對簡單高效野揪,但讓程序員手動根據(jù)字段進行逐一解析仍然是一件比較浪費時間的事情。在性能要求沒那么高的場景下(絕大多數(shù)情況)瞧栗,如果能實現(xiàn)c++對象和Json數(shù)據(jù)的自動轉(zhuǎn)換斯稳,無疑能大幅提高開發(fā)效率,并減少因程序員手誤導致的解析錯誤迹恐。

因此挣惰,考慮基于nlohmann/json解析庫,實現(xiàn)c++和Json數(shù)據(jù)的對象映射自動代碼生成。

nlohmann/json基礎

nlohmann/json是基于c++11特性實現(xiàn)的一個開源Json解析庫憎茂,其在github上的start數(shù)達到了13.4k珍语,開源協(xié)議為MIT license, 因此可以作為商用項目使用。整個解析庫只有一個json.hpp唇辨,可以非常方便的移植到項目程序中廊酣。且解析庫提供的接口非常人性化,上手容易赏枚,學習成本較低亡驰。
而對象映射這一功能,nolhmann庫其實已經(jīng)替我們實現(xiàn)了饿幅,舉官方的一個例子說明:

namespace ns {
    // a simple struct to model a person
    struct person {
        std::string name;
        std::string address;
        int age;
    };
}

// create a person
ns::person p {"Ned Flanders", "744 Evergreen Terrace", 60};

// conversion: person -> json
json j = p;

std::cout << j << std::endl;
// {"address":"744 Evergreen Terrace","age":60,"name":"Ned Flanders"}

// conversion: json -> person
auto p2 = j.get<ns::person>();

// that's it
assert(p == p2);
};

可以看到凡辱,程序當中,我們只需要定義好一個Person結構體栗恩,再定義一個json對象透乾,兩者即可用=進行隱式轉(zhuǎn)換。當然磕秤,實現(xiàn)隱式轉(zhuǎn)換的前提是定義相應的to_jsonfrom_json函數(shù)乳乌,該例中:

using nlohmann::json;

namespace ns {
    void to_json(json& j, const person& p) {
        j = json{{"name", p.name}, {"address", p.address}, {"age", p.age}};
    }

    void from_json(const json& j, person& p) {
        j.at("name").get_to(p.name);
        j.at("address").get_to(p.address);
        j.at("age").get_to(p.age);
    }
} // namespace ns

如上,我們得出基于nlohmann/json實現(xiàn)對象映射的核心步驟:

  1. 定義一個c++結構體
  2. 編寫該c++結構體轉(zhuǎn)換為json對象的to_json函數(shù)
  3. 編寫json對象轉(zhuǎn)換為該c++結構體的from_json函數(shù)

Python自動生成C++代碼

如上介紹市咆,對于一個現(xiàn)有的JSON數(shù)據(jù)汉操,我們還是需要編寫上述機械化的代碼,這些完全可以找出格式上的規(guī)則使用Python進行自動化代碼生成蒙兰。在github上搜索了相關項目后磷瘤,最終參考了一個項目的實現(xiàn)思路,使生成的代碼采用nlohmman/json進行解析搜变。

注: 原項目生成的代碼使用的JSON庫是<cppRest/json.h>采缚,項目鏈接kcris/json2cpp

具體的生成代碼不做詳述挠他,后面會將源碼Po上扳抽,有興趣的可以看一下≈城郑基本思想就是根據(jù)Json字段名進行類型區(qū)分摔蓝,對于對象類型進行遞歸生成。最終的生成結果采用一個Object對象類型對應一個.h頭文件和.cpp文件的形式愉耙。

C++解析贮尉、組裝函數(shù)封裝(可選)

nlohmann庫的隱式解析會拋出異常,我們需要捕獲異常并進行相應處理朴沿。因此猜谚,在cpp中考慮對這部分進行了二次封裝,使外層調(diào)用者不需要關心異常處理昌犹。此外斜姥,通信層傳輸?shù)腏SON格式有些是不帶外層節(jié)點的沧竟,有些是帶外層節(jié)點的悟泵,我們也需要對這兩種格式做適配糕非。

這部分有需要可以自己寫一下,沒有太多工作量禁筏。


快速開始

生成cpp文件

為了方便使用融师,基于tkinter做了一個界面蚁吝,打包成了一個EXE工具舀射。目前該工具只支持包含外層節(jié)點的JSON數(shù)據(jù)格式脆烟。

  1. 打開Json2cppTool.exe
  2. 填入JSON數(shù)據(jù)或者選擇JSON數(shù)據(jù)文件
  3. 選擇輸出路徑
  4. 點擊生成

JSON數(shù)據(jù)如下:

{
    "UserInfoDetail": {
        "mode": "",
        "EmployeeNoList": [
            {
                "employeeNo": ""
            }
        ]
    }
}

json2cpp.png

文件導入工程

生成文件如下:

UserInfoDetail.h
UserInfoDetail.cpp
EmployeeNoList.h
EmployeeNoList.cpp

C++程序中使用

序列化

UserInfoDetail user_info_detail ;
user_info_detail.m_mode = "all";
string str_json = JsonSerialize(user_info_detail);

反序列化

ResponseStatus response_status;
if (!JsonDeserialize(str_raw, response_status))
{
    return false;
}

That's it!

對于列表std::list<T>類型的節(jié)點驼抹,我們也無需做特殊處理框冀,nlohmann已經(jīng)將列表和JSON Array間的轉(zhuǎn)換實現(xiàn)掉了敏簿。

進階用法

序列化時控制是否輸出外層節(jié)點

默認會輸出外層節(jié)點,但是可以通過JsonSerialize(Obj,false)來指定不生成外層節(jié)點绣硝。

示例:

string str_json = JsonSerialize(user_info_detail, false);

輸出的JSON:

{ 
    "mode": "",
    "EmployeeNoList": [
        {
            "employeeNo": ""
        }
    ]
}

指定組裝的節(jié)點

在默認情況下鹉胖,自動映射會將c++結構體中的所有成員均映射到JSON中的節(jié)點甫菠。

但有的場景王带,我們希望發(fā)送給客戶端或服務端的JSON數(shù)據(jù)中渗鬼,只包含部分必填字段带迟。
自動生成的c++結構體中包含了一個std::set<std::string> m_visibleSet;成員仓犬,通過該成員控制需要輸出的節(jié)點舍肠。

示例:

UserInfoDetail user_info_detail ;
user_info_detail.m_mode = "all";
user_info_detail.m_visibleSet = {
    "mode",
};
string str_json = JsonSerialize(clearCfg);

輸出的JSON:

{
    "UserInfoDetail": {
        "mode": "all"
    }
}

指定需要忽略的節(jié)點

在默認情況下翠语,自動映射會將c++結構體中的所有成員均映射到JSON中的節(jié)點。

但有的場景点骑,我們希望發(fā)送給客戶端或服務端的JSON數(shù)據(jù)中黑滴,能忽略某些節(jié)點紧索。
自動生成的c++結構體中包含了一個std::set<std::string> m_hiddenSet;成員珠漂,通過該成員控制需要忽略的節(jié)點葛菇。

示例:

UserInfoDetail user_info_detail ;
user_info_detail.m_mode = "all";
user_info_detail.m_hiddenSet = {
    "EmployeeNoList",
};
string str_json = JsonSerialize(clearCfg);

輸出的JSON:

{
    "UserInfoDetail": {
        "mode": "all"
    }
}

附錄

源碼見json2cppTool

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市莺债,隨后出現(xiàn)的幾起案子签夭,更是在濱河造成了極大的恐慌第租,老刑警劉巖慎宾,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趟据,死亡現(xiàn)場離奇詭異汹碱,居然都是意外死亡,警方通過查閱死者的電腦和手機稚新,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來尺迂,“玉大人噪裕,你說我怎么就攤上這事膳音〖老荩” “怎么了兵志?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵悠栓,是天一觀的道長按价。 經(jīng)常有香客問我楼镐,道長框产,這世上最難降的妖魔是什么茅信? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任妖谴,我火速辦了婚禮膝舅,結果婚禮上窑多,老公的妹妹穿的比我還像新娘埂息。我一直安慰自己千康,他們只是感情好拾弃,可當我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布豪椿。 她就那樣靜靜地躺著咳秉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上户誓,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天帝美,我揣著相機與錄音,去河邊找鬼舰褪。 笑死疏橄,一個胖子當著我的面吹牛晃酒,可吹牛的內(nèi)容都是我干的贝次。 我是一名探鬼主播蛔翅,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼爽待!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起膏燃,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后泥畅,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體位仁,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡棠众,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片但骨。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡吼野,死狀恐怖闷哆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情抱怔,我是刑警寧澤劣坊,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站屈留,受9級特大地震影響局冰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜灌危,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一康二、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧勇蝙,春花似錦赠摇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惜傲,卻和暖如春洽故,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盗誊。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工时甚, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哈踱。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓荒适,卻偏偏與公主長得像,于是被迫代替她去往敵國和親开镣。 傳聞我的和親對象是個殘疾皇子刀诬,可洞房花燭夜當晚...
    茶點故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,103評論 1 32
  • 國家電網(wǎng)公司企業(yè)標準(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報批稿:20170802 前言: 排版 ...
    庭說閱讀 10,975評論 6 13
  • feisky云計算、虛擬化與Linux技術筆記posts - 1014, comments - 298, trac...
    不排版閱讀 3,855評論 0 5
  • 一. XML數(shù)據(jù)交換格式 XML數(shù)據(jù)交換格式是一種自描述的數(shù)據(jù)交互格式邪财,雖然XML數(shù)據(jù)格式不如JSON "輕便"陕壹,...
    __season____閱讀 2,512評論 0 7
  • 每天早上,行走在隊伍之中
    然2007閱讀 137評論 0 0