C++类结构体与json相互转换

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(@"{"Input":"1","Output":"2"}");

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

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

需求如下:

  • 只有头文件,方便使用
  • 最多三行代码解决转换
  • 支持类|结构体 与 json的相互转换
  • 支持多种基本数据类型,如int、float、string、bool等
  • 支持STL基本类型,如vector、list、map
  • 支持嵌套关系
  • 支持成员重命名,比方说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. 实现方法

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

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

//其他类型... ...

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

template
static bool WriteMembers(std::vector &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
static bool WriteMembers(std::vector &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 &names) \
{     \
    if (names.size()

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

template
struct enable_if
{
};

template
struct enable_if
{
    typedef TYPE type;
};

template
struct HasConverFunction
{
    template  static char func(decltype(&TT::AIGC_CONVER_JSON_TO_OBJECT));
    template  static int func(...);
    const static bool has = (sizeof(func(NULL)) == sizeof(char));
};

template ::has, int>::type = 0>
static inline bool JsonToObject(T &obj, rapidjson::Value &jsonValue)
{
    std::vector names = LoadRenameArray(obj);
    return obj.AIGC_CONVER_JSON_TO_OBJECT(jsonValue, names);
}

template ::has, int>::type = 0>
static inline bool JsonToObject(T &obj, rapidjson::Value &jsonValue)
{
    return false;
}
/**
 * @brief conver json string to class | struct
 * @param obj : class or struct
 * @param jsonStr : json string
 */
template
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. 学习与引用

Original: https://www.cnblogs.com/yaronzz/p/15147051.html
Author: Yaronzz
Title: C++类结构体与json相互转换

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585309/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 防火墙NAT+DHCP+ACL+ACAP

    任务要求: SwitchA作为有线终端网关与DHCP Server,为无线终端与有线终端分配IP地址,并配置ACL访问控制列表控制不同用户的访问权限,客户机只能跟DMZ区域服务器互…

    Linux 2023年6月7日
    084
  • Redis基础教程

    redis基础教程 1、 string数据结构 a) SET server:name “fido” b) SETNX server:name1″…

    Linux 2023年5月28日
    089
  • JS 模块化- 04 CMD 规范与 Sea JS

    1 CMD 规范介绍 CMD: Common Module Definition, 通用模块定义。与 AMD 规范类似,也是用于浏览器端,异步加载模块,一个文件就是一个模块,当模块…

    Linux 2023年6月6日
    0116
  • Vector底层实现

    vector的三个私有成员 :_start 记录初始位置, _finish 记录有效字符, _endofstoage 记录容量大小 vector会存储的类型不同,所以要用模版来定类…

    Linux 2023年6月13日
    0104
  • Linux基线加固

    bash;gutter:true; 1、修改vsftp回显信息 (1)检查办法 修改vsftp回显信息: 需在安装VSFTP的情况下检查,未安装可忽略或禁用该项。 查看ftpd_b…

    Linux 2023年6月13日
    090
  • 函数式编程

    1 概述 2 Lambda表达式 3 Stream流 // 创建stream的方法 //1 使用Collection下的 stream() 和 parallelStream() 方…

    Linux 2023年6月7日
    095
  • redis查看状态信息

    redis查看状态信息 info all|default Info 指定项 server服务器信息 redis_version : Redis 服务器版本 redis_git_sh…

    Linux 2023年5月28日
    0105
  • Linux 用户密码正确但无法登录和无法su问题故障

    一、 故障 描述 在应用登入操作系统时,用户密码正确但显示Permission denied无法登录,通过管理口控制台用root用户登录,也是同样情况,通过进入单用户查找问题。 一…

    Linux 2023年6月13日
    094
  • TCP 粘包-拆包问题及解决方案

    歧义在”TCP”上,这个”粘包”跟TCP其实没关系。这里的”粘包”其实是应用程序中没有处理好数据包分割,两个…

    Linux 2023年6月13日
    0116
  • ServiceHub.DataWarehouseHost.exe内存泄漏问题的处理

    ServiceHub.DataWarehouseHost.exe内存泄漏问题的处理。 Visual Studio 2017的15.2版本在debug应用程序时,ServiceHub…

    Linux 2023年6月7日
    0115
  • Docker简介与安装

    Docker简介与安装 Docker overview | Docker Documentation 官方文档 1.Docker简介 Docker是一个用于开发、发布和运行应用程序…

    Linux 2023年6月13日
    090
  • Ubuntu 20.04 双系统安装完整教程

    1、查看电脑的信息 1.1 查看BIOS模式 “win+r”快捷键进入”运行”,输入”msinfo32″回车…

    Linux 2023年6月7日
    0104
  • 【Redis】缓存穿透、缓存击穿、缓存雪崩产生原因及解决方案

    一. 本文对Redis中[缓存穿透]、[缓存击穿]、[缓存雪崩]三种现象产生原因、解决方法进行说明 二. 缓存穿透 1. 原因 2. 解决方法 三. 缓存击穿 1. 原因 2. 解…

    Linux 2023年5月28日
    0111
  • redis研究记录

    redis研究记录 1 概述目前多数的NoSql数据库本质上都是键值对形式,Redis也不例外。作为缓存数据库的一种,和Memcached相比,有以下几种主要的优点:(1)速度上,…

    Linux 2023年5月28日
    085
  • [Linux]iptables防火墙

    一、iptables介绍 二、表(Table) 三、链(Chain) 四、规则(Rule) 五、iptables规则的增删改查 一、iptables介绍 iptables是一个针对…

    Linux 2023年6月13日
    0118
  • Emacs Lisp 入门

    ;; This gives an introduction to Emacs Lisp in 15 minutes (v0.2d);;;; 英文原作者: Bastien / @bz…

    Linux 2023年6月13日
    0115
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球