最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++ 輕量級對象JSON序列化實現(xiàn)詳情

 更新時間:2021年09月30日 09:38:37   作者:楊昕  
本文以jsoncpp庫為基礎(chǔ),設(shè)計這樣一個可以支持一個函數(shù) 可以一行代碼 unmarshal /marshal 對象,需要的朋友小伙伴可以參考以下

背景:

在項目里經(jīng)常遇到對象和json字符串的相互轉(zhuǎn)換這類問題,在大多數(shù)程序里,一般這個問題都比較有比較好的解決方法,往往一個函數(shù)搞定。但是到了c++這邊卻需要我們手?jǐn)]json庫一個一個字段初始化/序列化。如果這時候有一個函數(shù) 可以一行代碼 unmarshal /marshal 對象豈不是很方便?本文以jsoncpp庫為基礎(chǔ),設(shè)計這樣一個可以支持這種功能的函數(shù),下面進(jìn)入正題~

1、設(shè)計思路

unmarshal 為例,我們最終的函數(shù)是打造兩個這樣的模板函數(shù) :

一個從string josn直接反序列化對象,一個從jsoncpp庫的json對象,反序列化對象。

template<typename T> bool Unmarshal(T& obj,const string& json_str);
template<typename T> bool Unmarshal(T& obj,const Json::Value& json_obj_root);


由于json是具有自遞歸結(jié)構(gòu)的,所以在設(shè)計時,應(yīng)該也是以遞歸的方式解決復(fù)雜的組合類,我們可以簡單的把程序中的變量分為下面幾類:

這樣我們只需要把這幾個場景的 Unmarshal實現(xiàn)了,整體的Unmarshal也就實現(xiàn)了。模板設(shè)計的類圖應(yīng)該和我們的分類相對應(yīng):

在實現(xiàn)中要注意以下幾點:

  • 每個分類的Unmarshal模板具有排他性,也就是說基本類型在編譯期間只能匹配解析基本類型的模板
  • 由于1的保證和這些模板函數(shù)名稱都是Unmarshal,所以他們之間可以相互嵌套調(diào)用對方。
  • 指針、和原生數(shù)組會涉及到空間分配和長度檢測會使得情況變得復(fù)雜,本次設(shè)計支持的范圍不包含對指針、原生數(shù)組的支持。

2、匹配基本類型的Unmarshal模板

//只能解析基本類型 int long bool float double string 的一組模板
/*
 * 其實可聲明為重載函數(shù),聲明為模板只是為了可以放在頭文件中
 * 不能聲明為 template <typename T> bool Unmarshal(int& obj,const Json::Value &root);是因為 
 * 在編譯時編譯器不能推斷出T的類型,導(dǎo)致編譯失敗.所以將模板類型列表設(shè)置為template <int = 0> (Nontype 
 * Parameters) 此處int并無實際意義
 */
template <int = 0> 
inline bool Unmarshal(int& obj,const Json::Value &root){
    if(!root.isIntegral())
        return false;
    obj = root.asInt();
    return true;
}

template <int = 0>
inline bool Unmarshal(long& obj,const Json::Value &root)

.....

3、匹配stl容器/其他第三方類庫的Unmarshal模板

//只能匹配 vector<T> map<string,T> map<long,T> map<int,T> 的一組模板
//vector
template <typename T>
bool Unmarshal(vector<T>& obj,const Json::Value& root){
    if(!root.isArray())
        return false;
    obj.clear();
    bool ret = true;
    for(int i=0;i<root.size();++i){
        T tmp;  //類型T要含有T()構(gòu)造函數(shù)
        if(!Unmarshal(tmp,root[i])) //遞歸調(diào)用Unmarshal函數(shù)
            ret = false;
        obj.push_back(tmp);
    }
    return ret;
}

//map key:string
template <typename T> 
bool Unmarshal(map<string,T>& obj,const Json::Value& root){
   ...
}

//map key:long
template <typename T> 
bool Unmarshal(map<long,T>& obj,const Json::Value& root){
   ...
}

//map key:int
template <typename T> 
bool Unmarshal(map<int,T>& obj,const Json::Value& root){
   ...
}

4、匹配自定義struct/class的Unmarshal模板

實現(xiàn)一組只能匹配自己定義的struct/class 就需要我們定義的對象有一些特殊的標(biāo)志,才能被模板函數(shù)識別。在這里選擇給我們自己定義的類都實現(xiàn)public的unmarshal方法(實現(xiàn)方式后面講),這樣當(dāng)編譯時發(fā)現(xiàn)一個對象含有 publicunmarshal方法時,就知道使是我們自己定義的類,然后就調(diào)用特定的模板函數(shù),這里用到到了一個C++的語法 SFINAE(Substitution Failure Is Not An Error) 和std庫中enable_if

我們先來看一下C++ std庫中 enable_if 的簡要實現(xiàn):

// 版本1 一個空的enable_if 結(jié)構(gòu)體
template <bool, class _Tp = void> 
struct enable_if {};

// 版本2 是版本1第一個參數(shù)為true的特例化實現(xiàn),這個版本的enable_if含有 type類型
template <class _Tp> 
struct enable_if<true, _Tp> {typedef _Tp type;};


int main(){
    enable_if<true,int>::type a = 3; //匹配版本2,相當(dāng)于 int a = 3
    enable_if<false,int>::type b = 3; //匹配版本1,enable_if{}沒有type類型,觸發(fā)編譯錯誤
}

SFINAE 準(zhǔn)則就是匹配失敗并不是錯誤,如果編譯器在匹配一個模板時引發(fā)了錯誤,這時候編譯器應(yīng)當(dāng)嘗試下一個匹配,而不應(yīng)該報錯中止。利用這條規(guī)則和enable_if,解析我們自己struct/class Umarshal模板設(shè)計如下:

// 檢測一個類 是否含有非靜態(tài)非重載的unmarshal方法
template<typename T>
struct TestUnmarshalFunc {

    //版本1 
    template<typename TT>
    static char func(decltype(&TT::unmarshal));

    //版本2
    template<typename TT>
    static int func(...);

    /*
     * 如果類型T沒有unmarshal方法,func<T>(NULL)匹配版本1時會產(chǎn)生錯誤,由于SFINAE準(zhǔn)則,只能匹配版本2 
     * 的func,此時返回值4個字節(jié),has變量為false.反之 has變量為true
     */
    const static bool has = (sizeof(func<T>(NULL)) == sizeof(char));
};


//如果對象自身含有 unmarshal 方法,則調(diào)用對象的unmarshal.否則會因SFINAE準(zhǔn)則跳過這個版本的Unamrshal
template <typename T,typename enable_if<TestUnmarshalFunc<T>::has,int>::type = 0>
inline bool Unmarshal(T& obj,const Json::Value &root){
    return obj.unmarshal(root);
}

好了,至此我們對三種基本類型的Umarshal函數(shù)設(shè)計好了,這時候任意一個T類型 在調(diào)用Unmarshal時,最終會與上面三種其中一個匹配。json 為string的可以利用上面的Unmarshal再封裝一個版本:

template <typename T>
bool Unmarshal(T& obj,const string &s){
    Json::Reader reader;
    Json::Value root;
    if(!reader.parse(s,root))
        return false;
    return Unmarshal(obj,root);
}


接下來我們看如何在自定義的類中實現(xiàn)unmarshal函數(shù):

//假設(shè)有一個People對象,有3個field需要反序列化,根據(jù)上面的要求,可能需要我們自己編寫unmarshal如下
struct People{
    bool sex;
    int age;
    string name;

    //盡力解析每個field,只有全部正確解析才返回true
    bool unmarshal(const Json::Value &root){
        bool ret = true;
        if(!Json::Unmarshal(sex,root["sex"])){   
            ret = false;                            
        }
        if(!Json::Unmarshal(age,root["age"])){
            ret = false;
        }
        if(!Json::Unmarshal(name,root["name"])){
            ret = false;
        }
        return ret;
    }
};

顯然如果field數(shù)量很多,就很麻煩,而且解析每個field時,代碼格式非常相似,是否存在一個宏可以自動生成呢?答案是肯定的。talk is cheap,show me the code ,上代碼!

struct People{
    bool sex;
    int age;
    string name;

    //用JSON_HELP宏把需要序列化的field傳進(jìn)去,就自動在類里生成unmarshal、marshal函數(shù)
    JSON_HELP(sex,age,name) 
};

// JSON_HELP 是一個變參宏
#define JSON_HELP(...)          \
    UNMARSHAL_OBJ(__VA_ARGS__)  \    //這里生成unmarshal函數(shù)
    MARSHAL_OBJ(__VA_ARGS__)


/*
 * UNMARSHAL_OBJ中FOR_EACH宏第一個參數(shù)傳入一個函數(shù),第二個參數(shù)傳入一個list
 * 作用是對list中每個元素調(diào)用傳入的函數(shù),這里有點像python里高階函數(shù)map()的味道
 * 這樣就批量生成了下面的代碼結(jié)構(gòu):
 *      if(!Json::Unmarshal(field_1,root["field_1"])){
 *           ret = false;
 *       }
 *      if(!Json::Unmarshal(field_2,root["field_2"])){
 *          ret = false;
 *      }
 *      ... ..      
 */
#define UNMARSHAL_OBJ(...)                                  \
    bool unmarshal(const Json::Value& root){                \
        bool ret = true;                                    \
        FOR_EACH(__unmarshal_obj_each_field__,__VA_ARGS__)  \
        return ret;                                         \
    }

#define __unmarshal_obj_each_field__(field)         \
        if(!Json::Unmarshal(field,root[#field])){   \
            ret = false;                            \
        }



//###### FOR_EACH 實現(xiàn)#######
//作用:傳入一個函數(shù)func和一個list,把func用在list的每個元素上
#define FOR_EACH(func,...) \
    MACRO_CAT(__func_,COUNT(__VA_ARGS__))(func,__VA_ARGS__)

/*
 * FOR_EACH在實現(xiàn)中 COUNT宏用于統(tǒng)計參數(shù)個數(shù),返回一個數(shù)字,MACRO_CAT宏用于把兩個token連接起來,
 * 如果__VA_ARGS__有三個變量為a,b,c 那么這一步宏展開后為:
 * __func_3(func,a,b,c), 而__func_3 __func_2 __func_1 ... 定義如下
 * /

// 宏展開實現(xiàn)偽循環(huán)
/*
 * __func_3(func,a,b,c) 具體展開過程:
 * 第一次: __func_1(func,a) __func_2(func,b,c) 
 * 第二次: func(a)          __func_1(func,b)    __func_1(func,c)
 * 第三次: func(a)          func(b)             func(c)
 * 最終在a,b,c上都調(diào)用了一次傳入的func函數(shù)
 */
#define __func_1(func,member)     func(member);
#define __func_2(func,member,...) __func_1(func,member)  __func_1(func,__VA_ARGS__)
#define __func_3(func,member,...) __func_1(func,member)  __func_2(func,__VA_ARGS__)
#define __func_4(func,member,...) __func_1(func,member)  __func_3(func,__VA_ARGS__)
#define __func_5(func,member,...) __func_1(func,member)  __func_4(func,__VA_ARGS__)
... ...


//###### COUNT 宏實現(xiàn)#######
//作用: 返回傳入?yún)?shù)個數(shù). eg: COUNT(a,b,c)返回3
#define COUNT(...) __count__(0, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define __count__(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N

###### MACRO_CAT 宏實現(xiàn)#######
//作用: 將兩個token(可以是宏),連接在一起
#define MACRO_CAT(a,b)  __macro_cat__(a,b)
#define __macro_cat__(a,b)  a##b

5、測試

我們的Umarshal Marshal函數(shù)編寫好了,現(xiàn)在測試一下吧:

場景:有一個map對象存著教師的信息,每個教師又保存著ta教學(xué)生信息,數(shù)據(jù)結(jié)構(gòu)定義如下:

struct Student {
    long    id;
    bool    sex;
    double  score;
    string  name;

    JSON_HELP(id,sex,score,name)
};
struct Teacher {
    string          name;
    int             subject;
    vector<Student> stus;

    JSON_HELP(name,subject,stus)
};

map<string,Teacher> tchs;  //需要序列化和反序列化的對象

測試代碼:

// 對應(yīng)于結(jié)構(gòu) map<string,Teacher> 的json
string ori = R"(
{
  "Tea_1": {
    "name": "Tea_1",
    "subject": 3,
    "stus": [
      {
        "id": 201721020126,
        "sex": false,
        "score": 80,
        "name": "Stu.a"
      },
      {
        "id": 201101101537,
        "sex": true,
        "score": 0,
        "name": "Stu.b"
      }
    ]
  },
  "Tea_2": {
    "name": "Tea_2",
    "subject": 1,
    "stus": [
      {
        "id": 201521020128,
        "sex": true,
        "score": 59,
        "name": "Stu.c"
      }
    ]
  }
}
)";

int main() {
    map<string,Teacher> tchs;

    // 從json字符串反序列化對象
    bool ret = Json::Unmarshal(tchs,ori);
    if(!ret){
        cout<<"反序列失敗"<<endl;
        return 0;
    }else{
        cout<<"反序列成功"<<endl;
    }

    // 序列化對象到 json字符串
    cout<<"輸出對象序列化的json:"<<endl;
    string obj2json;
    Json::Marshal(tchs,obj2json);
    cout<<obj2json;
}

//##### 輸出結(jié)果#####
反序列成功
輸出對象序列化的json:
{"Tea_1":{"name":"Tea_1","stus":[{"id":201721020126,"name":"Stu.a","score":80.0,"sex":false},{

到此這篇關(guān)于C++ 輕量級對象JSON序列化實現(xiàn)詳情的文章就介紹到這了,更多相關(guān)C++ 輕量級對象JSON序列化實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言泛型選擇編程示例詳解

    C語言泛型選擇編程示例詳解

    這篇文章主要介紹了C語言泛型選擇編程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Matlab中圖像數(shù)字水印算法的原理與實現(xiàn)詳解

    Matlab中圖像數(shù)字水印算法的原理與實現(xiàn)詳解

    數(shù)字水印技術(shù)作為信息隱藏技術(shù)的一個重要分支,是將信息(水印)隱藏于數(shù)字圖像、視頻、音頻及文本文檔等數(shù)字媒體中,從而實現(xiàn)隱秘傳輸、存儲、標(biāo)注、身份識別、版權(quán)保護和防篡改等目的。本文就來講講圖像數(shù)字水印算法的原理與實現(xiàn),感興趣的可以了解一下
    2023-04-04
  • Opencv實現(xiàn)畫筆功能

    Opencv實現(xiàn)畫筆功能

    這篇文章主要為大家詳細(xì)介紹了Opencv實現(xiàn)畫筆功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • C++利用Opencv實現(xiàn)多個圓形檢測

    C++利用Opencv實現(xiàn)多個圓形檢測

    霍夫圓檢測是opencv中用來檢測圓的重要算法,簡單的說,霍夫圓檢測就是對圖像中的弧線做切線,再在切點位置做切線的垂線,然后看這些垂線能交于一點的個數(shù),這個在方法中是自己設(shè)定的
    2022-08-08
  • 詳解Visual Studio 2019(VS2019) 基本操作

    詳解Visual Studio 2019(VS2019) 基本操作

    這篇文章主要介紹了詳解Visual Studio 2019(VS2019) 基本操作,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • C語言簡明講解預(yù)編譯的使用

    C語言簡明講解預(yù)編譯的使用

    在C語言的程序中包括各種以符號#開頭的編譯指令,這些指令稱為預(yù)處理命令。預(yù)處理命令屬于C語言編譯器,而不是C語言的組成部分,通過預(yù)處理命令可擴展C語言程序設(shè)計的環(huán)境
    2022-05-05
  • 一文讀懂C++ 虛函數(shù) virtual

    一文讀懂C++ 虛函數(shù) virtual

    這篇文章主要介紹了C++ 虛函數(shù) virtual的相關(guān)資料,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • C語言鏈表實現(xiàn)商品庫存管理系統(tǒng)

    C語言鏈表實現(xiàn)商品庫存管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語言鏈表實現(xiàn)商品庫存管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • C++11智能指針中的 unique_ptr實例詳解

    C++11智能指針中的 unique_ptr實例詳解

    unique是獨特的、唯一的意思,故名思議,unique_ptr可以“獨占”地?fù)碛兴赶虻膶ο?,它提供一種嚴(yán)格意義上的所有權(quán)。這篇文章主要介紹了C++11智能指針中的 unique_ptr實例詳解,需要的朋友可以參考下
    2020-06-06
  • C語言中動態(tài)內(nèi)存管理初學(xué)者容易犯的6個錯誤分享

    C語言中動態(tài)內(nèi)存管理初學(xué)者容易犯的6個錯誤分享

    本篇文章主要介紹了初學(xué)者使用C語言中動態(tài)內(nèi)存管理的4個函數(shù)時最容易犯的6個錯誤,以及如何避免這些錯誤,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2023-04-04

最新評論

崇州市| 富裕县| 安溪县| 西安市| 滁州市| 义马市| 西充县| 昆明市| 海门市| 松原市| 牙克石市| 焦作市| 尤溪县| 二连浩特市| 岚皋县| 平昌县| 乌拉特前旗| 璧山县| 枣强县| 长乐市| 鹤峰县| 宁安市| 济源市| 宁强县| 资阳市| 元谋县| 崇文区| 大兴区| 墨脱县| 千阳县| 长沙市| 西乌珠穆沁旗| 南华县| 汝阳县| 如东县| 临夏县| 县级市| 泸西县| 瓦房店市| 和政县| 珲春市|