谷动谷力

 找回密码
 立即注册
查看: 914|回复: 0
打印 上一主题 下一主题
收起左侧

一种cJSON与结构体互转的方法

[复制链接]
跳转到指定楼层
楼主
发表于 2023-10-12 22:56:09 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 sunsili 于 2023-10-12 23:04 编辑

一种cJSON与结构体互转的方法
来自:CSDN,作者:NevermindZZT
前言
json是目前最为流行的文本数据传输格式,特别是在网络通信上广泛应用,随着物联网的兴起,在嵌入式设备上,也需要开始使用json进行数据传输,那么,如何快速简洁地用C语言进行json的序列化和反序列化呢?
当前,应用最广泛的C语言json解析库当属cJSON,但是,使用cJSON读json进行序列化和反序列化,需要根据key一个一个进行处理,会导致代码冗余,逻辑性不强,哪有没有更好的方法呢?

思路

在Android平台,一般会使用gson等工具解析json,这些工具将json直接映射成对象,在C语言上使用对象的概念,我们需要借助结构体,然而,最大的问题在于,C语言没有高级语言具有的反射机制,直接从json映射到结构体对象几乎是不可能的。

怎么解决呢,既然C语言没有反射机制,那么我们可以自己定义一套类似于反射的机制,这里我将其称之为结构体数据模型,在数据模型中,我们需要准确地描述结构体的特征,包括结构体各成员的名称,类型,在结构体中的偏移。

有了这些,我们可以在解析josn的时候,将解析得到的数据直接写入到对应的内存里面去,或者是在序列化的时候,直接从对应的内存中读取数据,进行处理。
实现

CSON正是采用上面说到的思路,使用数据模型对结构体进行描述,然后基于cJSON,根据数据模型进行解析,将解析得到的数据直接写入到对应的内存区域,从而实现从json到结构体对象的映射。CSON最基本的数据模型定义如下:typedef struct cson_model
  1. {
  2.     CsonType type;                      /**< 数据类型 */
  3.     char *key;                          /**< 元素键值 */
  4.     short offset;                       /**< 元素偏移 */
  5. } CsonModel;
复制代码

通过type描述结构体成员的数据类型,key描述该成员在json中对应的字段,offset描述该结构体成员在结构体中的偏移,CSON在解析json的时候,根据type调用相应的cJSON API并传递key作为参数,得到解析出的数据,然后根据offset将数据写入到对应的内存空间。比如说这样一个结构体:
  1. struct project
  2. {
  3.     int id;
  4.     char *name;
  5. }
复制代码

该结构体包含两个成员,对于成员id,我们使用数据模型对其进行描述
  1. {
  2. .type=CSON_TYPE_CHAR,
  3. key="id",
  4. offset=0
  5. }
复制代码

对于结构体的每个成员,都进行数据模型的定义,就可以得到一个完整的结构体数据模型,CSON会根据这个模型,进行解析。因为是通过直接写内存的方式,所以在写不同类型的量到内存中时,会多次用到强制转型,导致CSON中赋值的代码都类似于:*
  1. (int *)((int)obj + model.offset) = (int)csonDecodeNumber(json, model.key);
  2. 当然,上面说到的数据模型,只适用于基本数据类型的数据,对于子结构体,链表,数组等,需要对数据模型的定义进行扩充,有兴趣的朋友可以直接阅读CSON源码。CSON使用实例声明结构体:/** 项目结构体 */
  3. struct project
  4. {
  5.     int id;
  6.     char *name;
  7. };

  8. /** 仓库结构体 */
  9. struct hub
  10. {
  11.     int id;
  12.     char *user;
  13.     struct project *cson;
  14. };
复制代码

定义数据模型:对每一个需要使用cson的结构体,都需要定义相对应的数据模型
  1. /** 项目结构体数据模型 */
  2. CsonModel projectModel[] =
  3. {
  4.     CSON_MODEL_OBJ(struct project),
  5.     CSON_MODEL_INT(struct project, id),
  6.     CSON_MODEL_STRING(struct project, name),
  7. };

  8. /** 仓库结构体数据模型 */
  9. CsonModel hubModel[] =
  10. {
  11.     CSON_MODEL_OBJ(struct hub),
  12.     CSON_MODEL_INT(struct hub, id),
  13.     CSON_MODEL_STRING(struct hub, user),
  14.     CSON_MODEL_STRUCT(struct hub, cson, projectModel, sizeof(projectModel)/sizeof(CsonModel))
  15. };
复制代码

使用CSON解析:只需要定义好数据模型,就可以使用CSON读json进行序列化和反序列化
  1. void csonDemo(void)
  2. {
  3.     char *jsonDemo = "{\"id\": 1, \"user\": \"Letter\", \"cson\": {\"id\": 2, \"name\": \"cson\"}}";

  4.     /** 解析json */
  5.     struct hub *pHub = csonDecode(jsonDemo, hubModel, sizeof(hubModel)/sizeof(CsonModel));
  6.     printf("hub: id: %d, user: %s, project id: %d, project name: %s\r\n",
  7.         pHub->id, pHub->user, pHub->cson->id, pHub->cson->name);

  8.     /** 序列化对象 */
  9.     char *formatJson = csonEncodeFormatted(pHub, hubModel, sizeof(hubModel)/sizeof(CsonModel));
  10.     printf("format json: %s\r\n", formatJson);

  11.     /** 释放结构体对象 */
  12.     csonFree(pHub, hubModel, sizeof(hubModel)/sizeof(CsonModel));

  13.     /** 释放序列化生成的json字符串 */
  14.     csonFreeJson(formatJson);
  15. }
复制代码

运行结果:
  1. hub: id: 1, user: Letter, project id: 2, project name: cson
  2. format json: {
  3.         "id":   1,
  4.         "user": "Letter",
  5.         "cson": {
  6.                 "id":   2,
  7.                 "name": "cson"
  8.         }
  9. }
复制代码

可以看到,无论是解析json,还是序列化结构体到json,在使用CSON的情况下,都只需要一行代码就可以解决,同样的操作,在使用原生cJSON的情况下,你可能需要多次判断,解析元素。

CSON github地址
https://github.com/NevermindZZT/cson





+10
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|深圳市光明谷科技有限公司|光明谷商城|Sunshine Silicon Corpporation ( 粤ICP备14060730号|Sitemap

GMT+8, 2024-12-27 16:55 , Processed in 0.084869 second(s), 41 queries .

Powered by Discuz! X3.2 Licensed

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表