JSON數(shù)據(jù)轉(zhuǎn)C++結(jié)構(gòu)體

JSON數(shù)據(jù)自動(dòng)生成C++結(jié)構(gòu)體

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

背景

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

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

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

nlohmann/json基礎(chǔ)

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

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);
};

可以看到,程序當(dāng)中,我們只需要定義好一個(gè)Person結(jié)構(gòu)體,再定義一個(gè)json對(duì)象,兩者即可用=進(jìn)行隱式轉(zhuǎn)換。當(dāng)然,實(shí)現(xiàn)隱式轉(zhuǎn)換的前提是定義相應(yīng)的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實(shí)現(xiàn)對(duì)象映射的核心步驟:

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

Python自動(dòng)生成C++代碼

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

注: 原項(xiàng)目生成的代碼使用的JSON庫是<cppRest/json.h>,項(xiàng)目鏈接kcris/json2cpp。

具體的生成代碼不做詳述,后面會(huì)將源碼Po上,有興趣的可以看一下。基本思想就是根據(jù)Json字段名進(jìn)行類型區(qū)分,對(duì)于對(duì)象類型進(jìn)行遞歸生成。最終的生成結(jié)果采用一個(gè)Object對(duì)象類型對(duì)應(yīng)一個(gè).h頭文件和.cpp文件的形式。

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

nlohmann庫的隱式解析會(huì)拋出異常,我們需要捕獲異常并進(jìn)行相應(yīng)處理。因此,在cpp中考慮對(duì)這部分進(jìn)行了二次封裝,使外層調(diào)用者不需要關(guān)心異常處理。此外,通信層傳輸?shù)腏SON格式有些是不帶外層節(jié)點(diǎn)的,有些是帶外層節(jié)點(diǎn)的,我們也需要對(duì)這兩種格式做適配。

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


快速開始

生成cpp文件

為了方便使用,基于tkinter做了一個(gè)界面,打包成了一個(gè)EXE工具。目前該工具只支持包含外層節(jié)點(diǎn)的JSON數(shù)據(jù)格式。

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

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

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

json2cpp.png

文件導(dǎo)入工程

生成文件如下:

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!

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

進(jìn)階用法

序列化時(shí)控制是否輸出外層節(jié)點(diǎn)

默認(rèn)會(huì)輸出外層節(jié)點(diǎn),但是可以通過JsonSerialize(Obj,false)來指定不生成外層節(jié)點(diǎn)。

示例:

string str_json = JsonSerialize(user_info_detail, false);

輸出的JSON:

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

指定組裝的節(jié)點(diǎn)

在默認(rèn)情況下,自動(dòng)映射會(huì)將c++結(jié)構(gòu)體中的所有成員均映射到JSON中的節(jié)點(diǎn)。

但有的場(chǎng)景,我們希望發(fā)送給客戶端或服務(wù)端的JSON數(shù)據(jù)中,只包含部分必填字段。
自動(dòng)生成的c++結(jié)構(gòu)體中包含了一個(gè)std::set<std::string> m_visibleSet;成員,通過該成員控制需要輸出的節(jié)點(diǎn)。

示例:

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é)點(diǎn)

在默認(rèn)情況下,自動(dòng)映射會(huì)將c++結(jié)構(gòu)體中的所有成員均映射到JSON中的節(jié)點(diǎn)。

但有的場(chǎng)景,我們希望發(fā)送給客戶端或服務(wù)端的JSON數(shù)據(jù)中,能忽略某些節(jié)點(diǎn)。
自動(dòng)生成的c++結(jié)構(gòu)體中包含了一個(gè)std::set<std::string> m_hiddenSet;成員,通過該成員控制需要忽略的節(jié)點(diǎn)。

示例:

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容