FStruct是一个用于C++对象(结构体,STL容器等)和json/xml字符串之间进行转换的库。
采用非入侵方式,无需在原有结构体上进行修改,目前支持基础类型,结构体,以及vector,list,deque,set,map等复杂数据类型的序列化,支持JSON和XML两种数据格式,支持别名,支持忽略字段,等其他特性,最少两行代码即可完成转换。
使用过java或者go的人知道这些语言在进行序列化和反序列化是很容易的,对于C++而言,这是困难的,根本原因是C++不支持反射,虽然C++不支持反射,但是我们依旧可以通过自己的方式来保存对象元信息来实现序列化与反序列化,记得我在大二时用C++写的一个client-server小项目,自己规定了传输的数据格式(当时觉得自己解析Json很麻烦),第一个字段应该是什么,第二个字段应该是什么,正是因为没有方便的函数进行转换,我每次都需要使用非常繁琐的代码去拼出一个可以传递的字符串,是的,这样确实可以完成我想要的功能,但是我自己定的数据格式只适合自己用,这种方式长期必然行不通,而大多数人使用JSON和XML这两种数据格式来保存数据,如果我的项目想要使用这种大众化的数据格式,我又将重构我的代码。
我试着在github寻找一些用于C++的序列化与反序列的库,看看有没有什么办法可以帮助我快速把对象转变成JSON,我找到了一些类似的库,但是获得或多或少存在一些问题。
不太和我心意的设计:
使用者需要添加过多代码❌
采用入侵方式,需要改变原有的结构体❌
不需要入侵,但是在注册的时候需要一个一个指定类型❌
只支持基础类型组成的结构体转换❌
不支持别名(由于go的特性,在go中经常被使用)❌
不支持忽略字段❌
不支持指针类型(如果原项目中存在指针类型便需要指针类型)❌
长痛不如短痛, 自己动手写一个吧。(一开始想的简单了,说实话后面还是有些难度的,但是没关系,一项一项攻破。)我大概将想法分为下面几个部分
第一阶段分为下面几个部分:
支持由基础类型和Json互转✔️
支持由基础类型组成的数组和json互转✔️
支持由基础类型组合成结构体类型和Json互转✔️
支持由结构体包含结构体的类型和Json互转✔️
支持vector类型和json互转✔️
支持list类型和json互转✔️
支持map类型和json互转✔️
支持set类型和json互转✔️
支持deque类型和json相转✔️
第二阶段分为下面几个部分:
结构体多层嵌套(如果成员包括STL容器,则STL容器支持基本类型)✔️
对第一阶段所使用的接口进行优化,将六个接口整合为两个:FJson/FObject,方面调用✔️
第三阶段分为下面几个部分:
支持对json字符串进行格式正确判断
支持获取某个字段是否存在
支持获取某个字段的值,而无须先进行序列化
结构体多层嵌套(如果成员包括STL容器,则STL容器支持基本类型)
第四阶段分为下面几个部分:
支持必选字段和可选字段,当必选字段无值时,进行报错(定义为指针类型即为可选字段)
支持XML数据格式的转换
杂项支持:
支持别名✔️
支持字段忽略✔️
支持忽略大小写✔️
支持字段为空,则不进行序列化
支持模糊转换
//测试所用结构体在example/testType.h中定义
//textType.h
struct student{
string name;
int age;
};
struct teacher{
string name;
int age;
};
//假设学校只有两个人
struct school{
student stu;
teacher tea;
};
//将需要定义结构体的头文件添加在definition.h头文件中
//definition.h 添加结构体,定义结构体
#include "example/testType.h"
//用于示例2, 并非需要同时添加,如果你确定只需要单向转换,那么可以只定义一方即可。
//这个用于结构体转json
#define Serialize_type_judgment_all\
Serialize_type_judgment(student)\
Serialize_type_judgment(teacher)\
//这个用于json转结构体
#define DesSerialize_type_judgment_all\
DesSerialize_type_judgment(student)\
DesSerialize_type_judgment(teacher)\
//测试文件
//example.cpp
#include "../FStruct.h" //添加序列化所需头文件
int main(){
//1.结构体只包含基础类型(int,char,char*,string,以及由基础类型构成的数组,或者是STL容器(map暂不支持全类型)),则只需要注册成员即可。
REGISTEREDMEMBER(student, name, age); //注册student成员
student stu;
stu.name = "yujing";
stu.age = 21;
string stu_json = "";
//结构体转json
Fdog::FJson(stu_json, stu); //结果 输出stu_json为: {"name":"yujing","age":21}
student stu2;
string stu2_json = "{\"name\":\"zhangxv\",\"age\":21}"; //引号要做处理
//json转结构体
Fdog::FObject(stu2, stu2_json); //结果 stu2.name = zhangxv stu2.age = 21
//2.结构体中除了基础类型,还包括自定义结构体
REGISTEREDMEMBER(teacher, name, age); //注册teacher成员
REGISTEREDMEMBER(school, stu, tea); //注册school成员
school sch;
sch.stu.name = "liuliu";
sch.stu.age = 18;
sch.tea.name = "wufang";
sch.tea.age = 48;
string sch_json = "";
//结构体转json
Fdog::FJson(sch_json, sch); //结果 输出sch_json为:{"stu":{"name":"liuliu","age":18},"tea":{"name":"wufang","age":48}}
//json转结构体
school sch2;
string sch2_json = "{\"stu\":{\"name\":\"liuliu\",\"age\":18},\"tea\":{\"name\":\"wufang\",\"age\":48}}";
Fdog::FObject(sch2, sch2_json);
//3.结构体成员存在自定义类型的数组
//马上支持
//4.结构体成员存在自定义类型STL容器
//马上支持
//5.支持别名(这个接口调用太复杂,后期会优化)
FdogSerialize::Instance()->setAliasName("student", "name", "Aliasname"); //第一个参数为类型,第二参数为原名,第三个参数为别名
Fdog::FJson(stu_json, stu); //结果 输出stu_json为: {"Aliasname":"yujing","age":21}
//6.支持字段忽略(这个接口调用太复杂,后期会优化)
FdogSerialize::Instance()->setIgnoreField("student", "name"); //第一个参数为类型,第二参数为需要忽略的字段
Fdog::FJson(stu_json, stu); //结果 输出stu_json为: {"age":21} //name字段的数据将被忽略
//7.支持忽略字段大小写(这个接口调用太复杂,后期会优化)
//当将json转为对象时,如json中的键值与对象中的成员名存在大小写不同,可以设定忽略大小写。
FdogSerialize::Instance()->setIgnoreLU("student", "name");
FdogSerialize::Instance()->setIgnoreLU("student", "age");
string stu_json = "{\"Name\":\"yujing\", \"AGE\":21}";
Fdog::FObject(stu, stu_json); //将Name对应name,AGE对应age
//8.针对5,6,7接口增加对应的一次性接口,避免有多个字段需要设置,从而多次调用接口
//下个版本支持
//9.默认支持模糊匹配
//马上支持,当不小心写错字段名时,程序将自动进行模糊匹配,最大可能完成转换。
//10.检测Json格式是否正确
//马上支持
//11.查找json中某个字段是否存在
//马上支持
//12.支持获取某个字段的值(返回类型支持int, double, string, bool)
//马上支持
//13.支持其他类型指针(指针类型将拥有可选字段属性,对于指针变量,在转换时,将先判断指针地址是否为空,若为空,将不进行转换,类似于忽略字段)
//下个版本
//14.支持xml序列化
//下下版本~
return 0;
}
源码地址:FStruct序列化库
由于FStruct出生时间有限,后续功能将慢慢完善!