https://github.com/sunchb/cson.git

目录

  • 前言
  • 示例
  • 实现
    • 1.实现“反射”
      • 1.1.描述结构体属性
      • 1.2.访问结构体属性
      • 1.3.结构体属性赋值
      • 1.3.数组和结构体类型的描述
    • 2.序列化
    • 3.反序列化
  • 代码及Demo

前言

之前用C语言解析过json,虽然借助jansson这样强大的解析库,可以将字符串转化为json对象。但是最终我们还是要一个字段一个字段的取值,当json对象较多,对象嵌套层次有比较深时,代码书写起来非常繁琐,动辄一个解析函数两三百行。

最近在做android开发,使用json与服务器进行数据交换。发现了gson这个好东西(原谅我,刚刚干java)——只需定义属性名和json字段名一致的java bean,然后调用gson.toJson或者fromJson就能完成正反序列化。使用起来真是爽到极点。gson使用了java的反射机制,通过迭代属性列表,直接将json中相应字段的值,赋值给对象的属性。

可不可以把这个思想用c语言实现呢?

示例

先举个例子,演示一下如何使用cson解析下面这样的一个json串。

{"name": "jay zhou","creater": "dahuaxia","songList": [{"songName": "qilixiang","signerName": "jay zhou","albumName": "qilixiang"},{"songName": "dongfengpo","signerName": "jay zhou","albumName": "dongfengpo"}]
}

大致分为以下三步:

  1. 定义与Json协议对应的结构体。(即使不用cson,大概也要这么做)
  2. 最最关键的一步,定义结构体的属性描述表。(由于C语言不像java那样本身就支持反射,所以我们通过定义属性描述表来描述一个结构体)
  3. 调用cson接口完成转换。

/*Step1:定义与json相对应的数据结构
*/
typedef struct {char*       songName;char*       signerName;char*       albumName;
} SongInfo;typedef struct {char*       name;char*       creater;size_t      songNum;SongInfo*   songList;
} PlayList;/*Step2:定义数据结构的反射表
*/
reflect_item_t song_ref_tbl[] = {_property_string(SongInfo, songName),_property_string(SongInfo, signerName),_property_string(SongInfo, albumName),_property_end()
};reflect_item_t play_list_ref_tbl[] = {_property_string(PlayList, name),_property_string(PlayList, creater),_property_int_ex(PlayList, songNum, _ex_args_all),_property_array_object(PlayList, songList, song_ref_tbl, SongInfo, songNum),    //此处是一个SongInfo类型的结构体数组,所以要关联上SongInfo类型的描述表。_property_end()
};/*Step3:调用csonJsonStr2Struct实现反序列化
*/
void test()
{const static char* jStr = "{\"name\":\"jay zhou\",\"creater\":\"dahuaxia\",\"songList\":[{\"songName\":\"qilixiang\",\"signerName\":\"jay zhou\",\"albumName\":\"qilixiang\"},{\"songName\":\"dongfengpo\",\"signerName\":\"jay zhou\",\"albumName\":\"dongfengpo\"}]}";PlayList playList;memset(&playList, 0, sizeof(playList));/* 将Json解析为结构体,一行代码完成解析 */csonJsonStr2Struct(jStr, &playList, play_list_ref_tbl);/* 测试输出结构体各属性名称和值 */csonPrintProperty(&playList, play_list_ref_tbl);/* 释放为数组和字符串申请的内存 */csonFreePointer(&playList, play_list_ref_tbl);
}

输出结果如下:

如果不使用cson,直接使用jannson或者cjson的话,就要逐个字段去解析,写起来会很麻烦。
如果字段较多,使用cson的好处就更加明显了。
(脑补一下100个字段,对象又嵌套个5,6层的时候,没有个500行代码估计解析不完)


如果想要了解cson内部实现,建议继续阅读下面内容。

实现

  • 让C语言结构体支持“反射”,即可以通过属性名访问属性值;
  • 序列化:循环访问C语言属性,使用Jansson或者cJSON库为每一个属性构建json对象,最终输出json字符串;
Struct
Jansson/cJSON Object
Json String
序列化
cson
Jansson/cJSON
End
  • 反序列化:使用Jansson或者cJSON库解析json字符串,得到Json对象。循环访问C语言的属性,并在Json对象中查找与属性名相同的json子对象,取值并赋值到结构体属性中;
Json String
Jansson/cJSON Objects
Struct
反序列化
Jansson/cJSON
cson
End

1.实现“反射”

1.1.描述结构体属性

例如下面这样一个简单的结构体,该如何使用“反射”去访问呢?

struct Info{int          i;char          c;double            d;char*             str;
};struct Info testInfo = {10000, 1, 0.88, "abc"};

要想实现类似java中反射这样的功能,第一步要做的是定义用于描述结构体属性的结构。

首先我们要知道属性的名字,属性在结构体中的地址,以及大小和类型;

typedef enum {CSON_STRING,CSON_INTEGER,CSON_REAL,
} cson_type;typedef struct reflect_item_t {char*                   field;                  /**< field */size_t                  offset;                 /**< offset of property */size_t                  size;                   /**< size of property */cson_type               type;                   /**< corresponding json type */
}reflect_item_t ;

用上面的结构描述结构体中每一个属性,这样就完成了对结构体的描述。

struct reflect_item_t InfoReflectTbl[] = {/*fiedl       offset                          size            type*/{"i",   (&(((struct Info*)0)->i)),       sizeof(int),    CSON_INTEGER},{"c",   (&(((struct Info*)0)->c)),       sizeof(char),   CSON_INTEGER},{"d"    (&(((struct Info*)0)->d)),       sizeof(double), CSON_REAL},{"str"  (&(((struct Info*)0)->str)),    sizeof(char*),  CSON_REAL}
};

1.2.访问结构体属性

这一步我们要实现的是,通过给定的属性名和对象,返回属性所在的地址。

函数原型如下:

/*** @brief get the address of field by retrieve the property table.** @param obj: object to be operated.* @param field:* @param tbl: property table of the type.** @return address of field.*/
void*   csonGetProperty(void* obj, const char* field, const reflect_item_t* tbl);

函数实现也简单,先通过属性名,在属性描述表tbl中找到对应的属性,返回obj + offset就可以了。

1.3.结构体属性赋值

函数原型如下:

/*** @brief set the field of object to specified data.** @param obj: object to be operated.* @param field:* @param data: pointer of specified data.* @param tbl: property table of the type.** @return void.*/
void    csonSetProperty(void* obj, const char* field, void* data, const reflect_item_t* tbl);

实现:先通过属性名,在属性描述表tbl中找到对应的属性;然后将data拷贝至obj + offset。

1.3.数组和结构体类型的描述

通常我们使用的结构体要比这个例子复杂的多。往往会包含其他结构体,或者是基本类型数组,或是结构体类型数组。这样的话我们就需要对上面的reflect_item_t结构进行一些扩充。

增加了三个属性:

  • reflect_tbl:如果属性为结构体或是结构体数组,需要设置为该类型的属性描述表。
  • arrayItemSize:如果属性为数组,需要指定单个数组元素的大小。因为需要为数组动态开辟内存空间。
  • arrayCountField:指定用于设定数组大小的字段。(我们解析json数组对象时,我们除了获取数组元素外,还需要保存数组大小。在c语言环境,我们需要数组大小来确保访问不会越界)
typedef struct reflect_item_t {char*                   field;                  /**< field */size_t                  offset;                 /**< offset of property */size_t                  size;                   /**< size of property */cson_type               type;                   /**< corresponding json type */const struct reflect_item_t*  reflect_tbl;      /**< reflect_tbl of sub-object. must be specified when type is object or object array. */size_t                  arrayItemSize;          /**< size of per array item. must be specified when type is array */char*                   arrayCountField;        /**< field saving array size */
} reflect_item_t;

举个例子:

typedef struct Info{int          i;char          c;double            d;char*             str;
}Info;typedef struct ExtData{int                    infoCount;  /* 用于保存数组infos的大小 */Info*                   infos;      /* 结构体数组 */
}ExtData;//描述表Info
struct reflect_item_t ExtDataReflectTbl[] = {/*fiedl       offset                          size            type        reflect_tbl     arrayItemSize   arrayCountField*/{"i",    (&(((struct Info*)0)->i)),       sizeof(int),    CSON_INTEGER,   NULL,           0,              NULL},{"c",   (&(((struct Info*)0)->c)),       sizeof(char),   CSON_INTEGER,   NULL,           0,              NULL},{"d"    (&(((struct Info*)0)->d)),       sizeof(double), CSON_REAL,      NULL,           0,              NULL},{"str"  (&(((struct Info*)0)->str)),     sizeof(char*),  CSON_REAL,      NULL,           0,              NULL}
};//描述表ExtData
struct reflect_item_t InfoReflectTbl[] = {/*fiedl      offset                          size            type        reflect_tbl     arrayItemSize   arrayCountField*/{"infoCount",(&(((ExtData*)0)->infoCount)), sizeof(int),  CSON_INTEGER,   NULL,           0,              NULL},{"infos"    (&(((ExtData*)0)->infos)),       sizeof(Info), CSON_OBJECT,  ExtDataReflectTbl,sizeof(Info), "infoCount"}
};

2.序列化

有了前面的准备工作,这一步就不算复杂了。

  • 创建根json对象;(可以使用jansson或是cJSON库)
  • 从属性描述表中循环取出每一个属性,再根据类型创建json对象放入根json对象中;
  • 将json根对象转为json字符串;

函数原型:

/*** @brief convert struct object to json string.** @param jstr: output json string* @param input: input struct object* @param tbl: property table of input.** @return ERR_NONE on success, otherwise failed.*/
int csonStruct2JsonStr(char** jstr, void* input, const reflect_item_t* tbl);

3.反序列化

和序列化差不多

  • 先将json字符串解析为json对象
  • 从属性描述表中循环取出每一个属性,再根据类型从json对象中取出对应的字段并赋值;
/*** @brief convert json string to struct object.** @param jstr: json string* @param output: target object* @param tbl: property table of output.** @return ERR_NONE on success, otherwise failed.*/
int csonJsonStr2Struct(const char* jstr, void* output, const reflect_item_t* tbl);

代码及Demo

cson

C语言实现Json与结构体相互转换——cson相关推荐

  1. Go 语言编程 — 高级数据类型 — 结构体

    目录 文章目录 目录 结构体 访问结构体成员 向函数传递结构体 结构体指针 结构体标签(Struct Tag) 结构体 Golang 中,结构体是由一系列具有相同类型或不同类型的数据构成的数据集合.与 ...

  2. OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)...

    OpenGL ES着色器语言之语句和结构体(官方文档第六章) OpenGL ES着色器语言的程序块基本构成如下: 语句和声明 函数定义 选择(if-else) 迭代(for, while, do-wh ...

  3. C语言定义了一个结构体怎么分配内存?C\C++中结构体变量与结构体指针内存分配问题?

    C语言定义了一个结构体怎么分配内存?C\C++中结构体变量与结构体指针内存分配问题? 问题1:结构体指针最开始怎么分配内存?用sizeof()是不能确定大小的. 问题2:给结构体变量分配之后,是否还要 ...

  4. c语言学习笔记【结构体02】结构体指针变量与结构体变量的函数参数,C语言学习笔记结构体02结构体指针变量与结构体变量的函数参数.docx...

    C 语言学习笔记[结构体02]结构体指针变量与结构体变量 的函数参数 C 语言学习笔记之结构体指针变量一提指针,那可 是 C 语言的核心了,有多少学子曾拜倒在指针的脚下.单纯的说指针,其实并不难,但是 ...

  5. c语言程序设计实验8,C语言程序设计实验八结构体.doc

    C语言程序设计实验八结构体 C语言程序设计实验八:结构体 请求用户输入若干个学生的基本情况,其中包括每位学生的学号.姓名和两门课(C语言.数学)的成绩.然后用下面的格式输出学生的成绩列表. 格式描述如 ...

  6. c语言如何宏定义枚举型结构体,C语言学习笔记--枚举结构体

    枚举 枚举是一种用户定义的数据类型,它用关键字enum以如下语法格式来声明: enum 枚举类型名字 {名字0,名字1,...,名字n}: 枚举类型名字通常并不真的使用,要用的是大括号里面的名字,因为 ...

  7. C语言的几种结构体Struct的赋值方法

    C语言的几种结构体Struct的赋值方法 1. 定义结构体类型 这里是两个结构体的基本结构. struct Init{int a;float b;double c; }I1; struct SIMPL ...

  8. c语言用typedef定义结构体,C语言结构体定义 typedef struct

    c语言规范,定义结构体: typedef struct ANSWER_HEADER { u8 u8Type; u8 u8Code; u32 u32TimeStamp; struct ANSWER_HE ...

  9. C++入门基础教程(一):C语言的指针与结构体到底怎么用?

    目录 一.前言 二.指针 2.1 指针与地址 2.2 指针与函数参数 2.3 指针与数组 2.3.1 指针与一维数组 2.3.2 指针与二维数组 2.4 字符指针与函数 2.5 指针数组以及指向指针的 ...

最新文章

  1. 静态联编与动态联编之virtual的作用
  2. icmp反弹shell 简介
  3. 大数系列之大数相加(ACM通过)(一)
  4. 30岁,没有月入过万算失败吗?用可视化分析30岁的人收入真相
  5. Android开发学习笔记:数据存取之File浅析
  6. 正在使用的文件如何删除?
  7. 学习C语言的必备书籍-从入门到精通
  8. c语言数列求和程序137,C语言循环结构 -C语言数列求和(使用while循环)
  9. 手机怎么设置腾达路由器后显示远端服务器,教你如何用手机快速设置腾达路由器...
  10. opencv实践中遇到的问题
  11. php网站整合ck播放器,wordpress整合ckplayer最新版 wp文章短代码调用ck播放器
  12. fluent周期边界_在Fluent中采用TUI设置周期性边界的方法
  13. Dungeon Master(地下城主)
  14. 音视频开发系列-H264编码原理
  15. java 仓库类_仓库类型和功能分别是什么?
  16. fastDfs+tracker+nginx在Centos7上配置文件服务器
  17. 多场景支付融合,刷脸支付入驻新华书店
  18. mysql Miscellaneous notes
  19. [Place 30-51] IDELAYCTRL elements have been found to be associated with IODELAY_GROUP ‘CAMERALINK‘,
  20. Fiddler 如何抓取手机app包以及抓取https 响应

热门文章

  1. 基于java框架springboot在线外卖点餐系统 java毕业设计课题选题毕设作品 毕业论文
  2. 如何将设计CAD图纸文件打印成纸质的图纸,并居中显示?
  3. WPF 控件专题 RadioButton详解
  4. WPF 组态软件实现思路(WPF控件可视化布局)
  5. 开机直接进入bios解决方法
  6. JavaScript开发简易音乐播放器
  7. MySQL 通配符过滤
  8. 连续第4年派息,动荡行情下挚文集团彰显稳健属性
  9. mysql 导入pdb_12c pdb的数据泵导入导出简单示例
  10. linux格式化u盘软件,linux下格式化U盘方法