JSON数据转C++结构体

JSON数据自动生成C++结构体

生成的c++结构体基于nlohmann/json进行解析,实现了类似JavaBean和C#中JsonConvert.SerializeObject的效果,将c++结构体与Json数据结构进行了映射,使得json解析成c++对象这一过程对上层屏蔽,可以实现快速开发。

背景

在编写服务端程序时,除了和系统交互、业务逻辑的内部实现,最主要的一部分就是和客户端打交道。现在web服务器开发,最流行的数据传输格式基本是Json、Xml、Protobuf,其中Json格式由于其和javascript语言对象模型的兼容性最好,成为b/s模型下最常用的数据传输格式。

在高级语言如Java、C#中,有一些内置的库实现了语言对象模型和Json数据间的自动转换,这一点着实让cpper羡慕不已。虽然c++也有一些成熟的开源解析库如nlohmann/jsonRapidJsonJsoncpp等,让解析Json已经变得相对简单高效,但让程序员手动根据字段进行逐一解析仍然是一件比较浪费时间的事情。在性能要求没那么高的场景下(绝大多数情况),如果能实现c++对象和Json数据的自动转换,无疑能大幅提高开发效率,并减少因程序员手误导致的解析错误。

因此,考虑基于nlohmann/json解析库,实现c++和Json数据的对象映射自动代码生成。

nlohmann/json基础

nlohmann/json是基于c++11特性实现的一个开源Json解析库,其在github上的start数达到了13.4k,开源协议为MIT license, 因此可以作为商用项目使用。整个解析库只有一个json.hpp,可以非常方便的移植到项目程序中。且解析库提供的接口非常人性化,上手容易,学习成本较低。
而对象映射这一功能,nolhmann库其实已经替我们实现了,举官方的一个例子说明:

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对象,两者即可用=进行隐式转换。当然,实现隐式转换的前提是定义相应的to_jsonfrom_json函数,该例中:

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实现对象映射的核心步骤:

  1. 定义一个c++结构体
  2. 编写该c++结构体转换为json对象的to_json函数
  3. 编写json对象转换为该c++结构体的from_json函数

Python自动生成C++代码

如上介绍,对于一个现有的JSON数据,我们还是需要编写上述机械化的代码,这些完全可以找出格式上的规则使用Python进行自动化代码生成。在github上搜索了相关项目后,最终参考了一个项目的实现思路,使生成的代码采用nlohmman/json进行解析。

注: 原项目生成的代码使用的JSON库是<cppRest/json.h>,项目链接kcris/json2cpp

具体的生成代码不做详述,后面会将源码Po上,有兴趣的可以看一下。基本思想就是根据Json字段名进行类型区分,对于对象类型进行递归生成。最终的生成结果采用一个Object对象类型对应一个.h头文件和.cpp文件的形式。

C++解析、组装函数封装(可选)

nlohmann库的隐式解析会抛出异常,我们需要捕获异常并进行相应处理。因此,在cpp中考虑对这部分进行了二次封装,使外层调用者不需要关心异常处理。此外,通信层传输的JSON格式有些是不带外层节点的,有些是带外层节点的,我们也需要对这两种格式做适配。

这部分有需要可以自己写一下,没有太多工作量。


快速开始

生成cpp文件

为了方便使用,基于tkinter做了一个界面,打包成了一个EXE工具。目前该工具只支持包含外层节点的JSON数据格式。

  1. 打开Json2cppTool.exe
  2. 填入JSON数据或者选择JSON数据文件
  3. 选择输出路径
  4. 点击生成

JSON数据如下:

{
    "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>类型的节点,我们也无需做特殊处理,nlohmann已经将列表和JSON Array间的转换实现掉了。

进阶用法

序列化时控制是否输出外层节点

默认会输出外层节点,但是可以通过JsonSerialize(Obj,false)来指定不生成外层节点。

示例:

string str_json = JsonSerialize(user_info_detail, false);

输出的JSON:

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

指定组装的节点

在默认情况下,自动映射会将c++结构体中的所有成员均映射到JSON中的节点。

但有的场景,我们希望发送给客户端或服务端的JSON数据中,只包含部分必填字段。
自动生成的c++结构体中包含了一个std::set<std::string> m_visibleSet;成员,通过该成员控制需要输出的节点。

示例:

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"
    }
}

指定需要忽略的节点

在默认情况下,自动映射会将c++结构体中的所有成员均映射到JSON中的节点。

但有的场景,我们希望发送给客户端或服务端的JSON数据中,能忽略某些节点。
自动生成的c++结构体中包含了一个std::set<std::string> m_hiddenSet;成员,通过该成员控制需要忽略的节点。

示例:

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,097评论 1 32
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,960评论 6 13
  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,844评论 0 5
  • 一. XML数据交换格式 XML数据交换格式是一种自描述的数据交互格式,虽然XML数据格式不如JSON "轻便",...
    __season____阅读 2,502评论 0 7
  • 每天早上,行走在队伍之中
    然2007阅读 137评论 0 0