1. 背景与需求

之前写C#的时候,解析json字符串一般使用的是开源的类库Newtonsoft.Json,方法十分简洁,比如:

class Project
{
public string Input { get; set; }
public string Output { get; set; }
}
JavaScriptSerializer serializer = new JavaScriptSerializer();
Project test = serializer.Deserialize<Project>(@"{"Input":"1","Output":"2"}");

一行代码就能将json字符串转为相应的类对象。

最近写C++需要处理一下json数据,于是上github找了很多很强大的开源库,像jsoncpprapidjsonjson,基本上都满足了开发需求,但想要做成像写C#那样子就要做二次开发。于是有了自己写一个简单的json转类 | 结构体的工具的想法(开源地址)。

需求如下:

  • 只有头文件,方便使用
  • 最多三行代码解决转换
  • 支持类|结构体 与 json的相互转换
  • 支持多种基本数据类型,如int、float、string、bool等
  • 支持STL基本类型,如vector、list、map<string,T>等
  • 支持嵌套关系
  • 支持成员重命名,比方说json中的关键字是name,成员命名可写成Name或其他。

2. 最终使用的样例代码

class Student
{
public:
string Name;
int Age; AIGC_JSON_HELPER(Name, Age)//成员注册
AIGC_JSON_HELPER_RENAME("name","age")//成员重命名,不需要可以删除这条
}; int main()
{
//json转类对象
Student person;
JsonHelper::JsonToObject(person, R"({"name":"XiaoMing", "age":15})");
//类对象转json
string jsonStr;
JsonHelper::ObjectToJson(person, jsonStr);
return 0;
}

3. 实现方法

因为刚好rapidjson只需要头文件就可以使用,所以选择了rapidjson作为基础库,进行二次开发。

3.1 基础类型的转换

作为最底层的接口,只需要进行一个赋值的操作即可,后续如果想要增加一些其他类型支持,添加起来也比较方便。

static bool JsonToObject(int &obj, rapidjson::Value &jsonValue)
{
if (jsonValue.IsNull() || !jsonValue.IsInt())
return false;
obj = jsonValue.GetInt();
return true;
} static bool JsonToObject(unsigned int &obj, rapidjson::Value &jsonValue)
{
if (jsonValue.IsNull() || !jsonValue.IsUint())
return false;
obj = jsonValue.GetUint();
return true;
} static bool JsonToObject(int64_t &obj, rapidjson::Value &jsonValue)
{
if (jsonValue.IsNull() || !jsonValue.IsInt64())
return false;
obj = jsonValue.GetInt64();
return true;
} //其他类型... ...

3.2 类成员注册

这里使用宏定义方式 + 可变参数模板的方式来实现,即可依次对注册的成员进行赋值

template <typename TYPE, typename... TYPES>
static bool WriteMembers(std::vector<std::string> &names, int index, rapidjson::Value &jsonValue, TYPE &arg, TYPES &... args)
{
if (!WriteMembers(names, index, jsonValue, arg))
return false;
return WriteMembers(names, ++index, jsonValue, args...);
} template <typename TYPE>
static bool WriteMembers(std::vector<std::string> &names, int index, rapidjson::Value &jsonValue, TYPE &arg)
{
const char *key = names[index].c_str();
if (!jsonValue.HasMember(key))
return true; if (!JsonToObject(arg, jsonValue[key]))
return false;
return true;
}
#define AIGC_JSON_HELPER(...)  \
bool AIGC_CONVER_JSON_TO_OBJECT(rapidjson::Value &jsonValue, std::vector<std::string> &names) \
{ \
if (names.size() <= 0) \
names = aigc::JsonHelper::GetMembersNames(#__VA_ARGS__); \
return aigc::JsonHelper::WriteMembers(names, 0, jsonValue, __VA_ARGS__); \
}

3.3 自定义类的转换

自定义类由于并不清楚外界使用时,是否有按规定添加好成员注册接口,所以这里采用enable_if的方式来尝试调用,编译的时候也就不会报错。

template <bool, class TYPE = void>
struct enable_if
{
}; template <class TYPE>
struct enable_if<true, TYPE>
{
typedef TYPE type;
}; template <typename T>
struct HasConverFunction
{
template <typename TT> static char func(decltype(&TT::AIGC_CONVER_JSON_TO_OBJECT));
template <typename TT> static int func(...);
const static bool has = (sizeof(func<T>(NULL)) == sizeof(char));
}; template <typename T, typename enable_if<HasConverFunction<T>::has, int>::type = 0>
static inline bool JsonToObject(T &obj, rapidjson::Value &jsonValue)
{
std::vector<std::string> names = LoadRenameArray(obj);
return obj.AIGC_CONVER_JSON_TO_OBJECT(jsonValue, names);
} template <typename T, typename enable_if<!HasConverFunction<T>::has, int>::type = 0>
static inline bool JsonToObject(T &obj, rapidjson::Value &jsonValue)
{
return false;
}

3.4 外部调用接口

/**
* @brief conver json string to class | struct
* @param obj : class or struct
* @param jsonStr : json string
*/
template <typename T>
static inline bool JsonToObject(T &obj, const std::string &jsonStr)
{
rapidjson::Document root;
root.Parse(jsonStr.c_str());
if (root.IsNull())
return false; return JsonToObject(obj, root);
}

最核心的部分也就上面的几个模块,其他的都是一些琐碎的增加类型支持等操作。

4. 学习与引用

  1. 腾讯json解析库:Tencent/rapidjson
  2. 杨昕: C++ 轻量级对象JSON序列化实现

C++类结构体与json相互转换的更多相关文章

  1. iOS 字典与JSON相互转换

    iOS 字典与JSON相互转换 首先简单说一下为什么会写这种幼稚的文章. 现在的网络请求几乎都是AFN完成的,AFN也为我们写了了JSON转换字典的方法,但是不要忘记后台是一个很爱用JSON的人群,H ...

  2. AngularJs:String类型和JSON相互转换

    最近一周做了一个页面,制作的过程中遇到各种问题,从中可以看出本人的js基础还不够扎实,angularjs也只是刚入门的水平,现在将制作过程中遇到的问题一一汇总,方便以后查阅. 一.String类型和J ...

  3. 类结构体 与 byte[] 转换类

    public static class StructConvert { public static object BytesToStruct(byte[] bytes, Type strcutType ...

  4. go语言之进阶篇通过结构体生成json

    1.通过结构体生成json 示例: package main import ( "encoding/json" "fmt" ) //成员变量名首字母必须大写 t ...

  5. @RequestBody 处理类型 和 对象 和 json 相互转换

    1 @RequestBody 处理类型 在项目中经常看到controller 中有 @RequestBody 字样,他到底有什么作用? 一般使用表单提交数据时不需要使用@RequestBody 即可自 ...

  6. 数据集和JSON相互转换

    使用DELPHI原生类实现数据集和JSON相互转换  JSON二要素:数组和对象.对象可以包含数组,数组可以包含对象.无层数限制.OLEVARIANT也类似,OLEVARIANT的一个元素又可以是OL ...

  7. Go语言结构体转json的坑

    Go语言结构体转json的坑 标签(空格分隔): go json.Marshal() JSON输出的时候必须注意,只有导出的字段(首字母是大写)才会被输出,如果修改字段名,那么就会发现什么都不会输出, ...

  8. Win32 程序开发:窗口类结构体 WNDCLASS 和 WNDCLASSEX

    一.窗口类结构体简介 窗口类结构体包含了窗口的各种参数信息.比如:窗口的图标.菜单栏.背景颜色.窗口的消息处理等等. 窗口类结构体有两个:WNDCLASS(早期版本) 和 WNDCLASSEX(新版本 ...

  9. struct2json -- C结构体与 JSON 快速互转库V1.0发布

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/zhutianlong/article/d ...

随机推荐

  1. linux0.01内核源码结构

    目录 boot 系统引导. fs 文件系统. include 头文件.一些C标准库,系统核心库. init 入口.main.c. kernel 内核. lib 库.C源程序,一些基本核心的程序. mm ...

  2. 面试必问题:JS防抖与节流

    摘要:防抖与节流可谓是面试常见,其实很好理解,下面带你分分钟了解防抖与节流的基本思想与写法~ 本文分享自华为云社区<JS防抖与节流快速了解与应用>,作者:北极光之夜. . 一.速识防抖: ...

  3. eh-admin一套前后端一体的轻量级后台管理系统

    https://gitee.com/DawnYang/eh-admin 主要技术 后端技术:Spring Boot,Apache Shiro,MyBatis-Plus等: 前端技术:Jquery,La ...

  4. 利用Python快速绘制海报级别地图

    1 简介 基于Python中诸如matplotlib等功能丰富.自由度极高的绘图库,我们可以完成各种极富艺术感的可视化作品,关于这一点我在系列文章在模仿中精进数据可视化中已经带大家学习过很多案例了. ...

  5. Springboot_Email注解爆红

    应该是更新后的版本,不会自动导入pom依赖 <!--新版本需要validation启动器 --> <dependency> <groupId>org.springf ...

  6. Shell 脚本如何输出帮助信息?

    作者展示了一个技巧,将帮助信息写在 Bash 脚本脚本的头部,然后只要执行"脚本名 + help",就能输出这段帮助信息 https://samizdat.dev/help-mes ...

  7. vue 点击复制当前网址

    template 部分 <div  class="NewNoticeDetails-ctrlButton" @click="copy()">     ...

  8. <题解>「LibreOJ NOIP Round #1」序列划分

    solutions 题面loj#542 对我来说,这或许已经超出了我的能力,我,只能看题解 不知道我写完这一篇题解之后,会不会对我的构造题有一点点的帮助 让我在这类题的解决上能过有一些提升 直接说明白 ...

  9. “ShardingCore”是如何针对分表下的分页进行优化的

    分表情况下的分页如何优化 首先还是要给自己的开原框架打个广告 sharding-core 针对efcore 2+版本的分表组件,首先我们来快速回顾下目前市面上分表下针对分页常见的集中解决方案 分表解决 ...

  10. MySQL——优化

    MySQL数据库优化: 1.优化角度 安全: 数据可持续性 性能: 数据的高性能访问 2.优化范围(优化顺序---->) (1)存储.主机和操作系统: 主机架构稳定性 I/O规划及配置 swap ...