千家信息网

C++如何实现轻量级对象JSON序列化

发表于:2025-02-06 作者:千家信息网编辑
千家信息网最后更新 2025年02月06日,这篇文章给大家分享的是有关C++如何实现轻量级对象JSON序列化的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1、设计思路以unmarshal 为例,我们最终的函数是打造两
千家信息网最后更新 2025年02月06日C++如何实现轻量级对象JSON序列化

这篇文章给大家分享的是有关C++如何实现轻量级对象JSON序列化的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

1、设计思路

unmarshal 为例,我们最终的函数是打造两个这样的模板函数 :

一个从stringjosn直接反序列化对象,一个从jsoncpp库的json对象,反序列化对象。

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

由于json是具有自递归结构的,所以在设计时,应该也是以递归的方式解决复杂的组合类,我们可以简单的把程序中的变量分为下面几类:

这样我们只需要把这几个场景的 Unmarshal实现了,整体的Unmarshal也就实现了。模板设计的类图应该和我们的分类相对应:

在实现中要注意以下几点:

  • 每个分类的Unmarshal模板具有排他性,也就是说基本类型在编译期间只能匹配解析基本类型的模板

  • 由于1的保证和这些模板函数名称都是Unmarshal,所以他们之间可以相互嵌套调用对方。

  • 指针、和原生数组会涉及到空间分配和长度检测会使得情况变得复杂,本次设计支持的范围不包含对指针、原生数组的支持。

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

//只能解析基本类型 int long bool float double string 的一组模板/* * 其实可声明为重载函数,声明为模板只是为了可以放在头文件中 * 不能声明为 template  bool Unmarshal(int& obj,const Json::Value &root);是因为  * 在编译时编译器不能推断出T的类型,导致编译失败.所以将模板类型列表设置为template  (Nontype  * Parameters) 此处int并无实际意义 */template  inline bool Unmarshal(int& obj,const Json::Value &root){    if(!root.isIntegral())        return false;    obj = root.asInt();    return true;}template inline bool Unmarshal(long& obj,const Json::Value &root).....

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

//只能匹配 vector map map map 的一组模板//vectortemplate bool Unmarshal(vector& obj,const Json::Value& root){    if(!root.isArray())        return false;    obj.clear();    bool ret = true;    for(int i=0;i bool Unmarshal(map& obj,const Json::Value& root){   ...}//map key:longtemplate  bool Unmarshal(map& obj,const Json::Value& root){   ...}//map key:inttemplate  bool Unmarshal(map& obj,const Json::Value& root){   ...}

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

实现一组只能匹配自己定义的struct/class 就需要我们定义的对象有一些特殊的标志,才能被模板函数识别。在这里选择给我们自己定义的类都实现public的unmarshal方法(实现方式后面讲),这样当编译时发现一个对象含有 publicunmarshal方法时,就知道使是我们自己定义的类,然后就调用特定的模板函数,这里用到到了一个C++的语法 SFINAE(Substitution Failure Is Not An Error) 和std库中enable_if

我们先来看一下C++ std库中 enable_if 的简要实现:

// 版本1 一个空的enable_if 结构体template  struct enable_if {};// 版本2 是版本1第一个参数为true的特例化实现,这个版本的enable_if含有 type类型template  struct enable_if {typedef _Tp type;};int main(){    enable_if::type a = 3; //匹配版本2,相当于 int a = 3    enable_if::type b = 3; //匹配版本1,enable_if{}没有type类型,触发编译错误}

SFINAE 准则就是匹配失败并不是错误,如果编译器在匹配一个模板时引发了错误,这时候编译器应当尝试下一个匹配,而不应该报错中止。利用这条规则和enable_if,解析我们自己struct/class Umarshal模板设计如下:

// 检测一个类 是否含有非静态非重载的unmarshal方法templatestruct TestUnmarshalFunc {    //版本1     template    static char func(decltype(&TT::unmarshal));    //版本2    template    static int func(...);    /*     * 如果类型T没有unmarshal方法,func(NULL)匹配版本1时会产生错误,由于SFINAE准则,只能匹配版本2      * 的func,此时返回值4个字节,has变量为false.反之 has变量为true     */    const static bool has = (sizeof(func(NULL)) == sizeof(char));};//如果对象自身含有 unmarshal 方法,则调用对象的unmarshal.否则会因SFINAE准则跳过这个版本的Unamrshaltemplate ::has,int>::type = 0>inline bool Unmarshal(T& obj,const Json::Value &root){    return obj.unmarshal(root);}

好了,至此我们对三种基本类型的Umarshal函数设计好了,这时候任意一个T类型 在调用Unmarshal时,最终会与上面三种其中一个匹配。json 为string的可以利用上面的Unmarshal再封装一个版本:

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

接下来我们看如何在自定义的类中实现unmarshal函数:

//假设有一个People对象,有3个field需要反序列化,根据上面的要求,可能需要我们自己编写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数量很多,就很麻烦,而且解析每个field时,代码格式非常相似,是否存在一个宏可以自动生成呢?答案是肯定的。talk is cheap,show me the code ,上代码!

struct People{    bool sex;    int age;    string name;    //用JSON_HELP宏把需要序列化的field传进去,就自动在类里生成unmarshal、marshal函数    JSON_HELP(sex,age,name) };// JSON_HELP 是一个变参宏#define JSON_HELP(...)          \    UNMARSHAL_OBJ(__VA_ARGS__)  \    //这里生成unmarshal函数    MARSHAL_OBJ(__VA_ARGS__)/* * UNMARSHAL_OBJ中FOR_EACH宏第一个参数传入一个函数,第二个参数传入一个list * 作用是对list中每个元素调用传入的函数,这里有点像python里高阶函数map()的味道 * 这样就批量生成了下面的代码结构: *      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 实现#######//作用:传入一个函数func和一个list,把func用在list的每个元素上#define FOR_EACH(func,...) \    MACRO_CAT(__func_,COUNT(__VA_ARGS__))(func,__VA_ARGS__)/* * FOR_EACH在实现中 COUNT宏用于统计参数个数,返回一个数字,MACRO_CAT宏用于把两个token连接起来, * 如果__VA_ARGS__有三个变量为a,b,c 那么这一步宏展开后为: * __func_3(func,a,b,c), 而__func_3 __func_2 __func_1 ... 定义如下 * /// 宏展开实现伪循环/* * __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上都调用了一次传入的func函数 */#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 宏实现#######//作用: 返回传入参数个数. 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 宏实现#######//作用: 将两个token(可以是宏),连接在一起#define MACRO_CAT(a,b)  __macro_cat__(a,b)#define __macro_cat__(a,b)  a##b

5、测试

我们的UmarshalMarshal函数编写好了,现在测试一下吧:

场景:有一个map对象存着教师的信息,每个教师又保存着ta教学生信息,数据结构定义如下:

struct Student {    long    id;    bool    sex;    double  score;    string  name;    JSON_HELP(id,sex,score,name)};struct Teacher {    string          name;    int             subject;    vector stus;    JSON_HELP(name,subject,stus)};map tchs;  //需要序列化和反序列化的对象

测试代码:

// 对应于结构 map 的jsonstring 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 tchs;    // 从json字符串反序列化对象    bool ret = Json::Unmarshal(tchs,ori);    if(!ret){        cout<<"反序列失败"<

感谢各位的阅读!关于"C++如何实现轻量级对象JSON序列化"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

函数 对象 模板 序列 版本 类型 编译 设计 参数 方法 结构 C++ 作用 变量 错误 生成 两个 代码 准则 编译器 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 安卓和鸿蒙是一个服务器吗 h410支持16g服务器内存吗 如何转换数据库格式文件 商务局网络安全重大事项报告制度 上海濮西互联网科技有限公司 大庆餐饮软件开发 阿里云服务器如何配置 上海oa软件开发哪家好 计算机网络技术专业做宽带 绝地求生未来之翼服务器维护者 预防针时间表软件开发 网络安全靠大家的文稿 服务器崩溃的名词说法 软件开发和实施与运维 倩女幽魂手游怎么增加服务器 计算机网络技术英语有影响吗 软件开发杭州市 数据库共有多少种数据类型 数据库中如何导出idb格式文件 工业网络技术与智能制造 数据库系统概论的试题 登录吃鸡显示连接服务器错误 奉贤区品质数据库服务商服务费 心灵终结怎么选服务器不卡 中国的电信网络技术 软件开发被甩锅给你怎么办 服务器的ip和端口 青少年预防网络安全视频 网络安全大赛真正的冠军 数据库设计视频教程
0