引言
JSON(JavaScript Object Notation)是目前最流行的文本数据传输格式,特别是在网络通信中广泛应用。随着物联网的兴起,嵌入式设备上也需要开始使用JSON进行数据传输。那么,在C语言中如何以快速简洁的方式进行JSON的序列化和反序列化呢?
目前,应用最广泛的C语言JSON解析库是cJSON。然而,使用cJSON读取JSON进行序列化和反序列化需要逐个处理每个键(key),会导致代码冗余并且缺乏逻辑性。是否有更好的解决方法呢?
思路
在Android平台上,通常会使用GSON等工具来解析JSON,这些工具可以直接将JSON映射为对象。在C语言中引入对象的概念需要借助结构体(structure)。然而,C语言缺少高级语言具备的反射机制,直接将JSON映射到结构体对象几乎是不可能的。
那么如何解决这个问题呢?既然C语言没有反射机制,我们可以自己定义一套类似于反射的机制,我将其称为结构体数据模型。在数据模型中,我们需要准确地描述结构体的特征,包括结构体各成员的名称、类型以及在结构体中的偏移量。
有了这些信息,我们可以在解析JSON时,直接将解析得到的数据写入到对应的内存中,或者在序列化时,直接从对应的内存中读取数据并进行处理。
实现
CSON正是采用上面说到的思路,使用数据模型对结构体进行描述,然后基于cJSON,根据数据模型进行解析,将解析得到的数据直接写入到对应的内存区域,从而实现从json到结构体对象的映射。
CSON最基本的数据模型定义如下:
typedef struct cson_model
{
CsonType type; /**
通过type描述结构体成员的数据类型,key描述该成员在json中对应的字段,offset描述该结构体成员在结构体中的偏移,CSON在解析json的时候,根据type调用相应的cJSON API并传递key作为参数,得到解析出的数据,然后根据offset将数据写入到对应的内存空间。
比如说这样一个结构体:
struct project
{
int id;
char *name;
}
该结构体包含两个成员,对于成员id,我们使用数据模型对其进行描述
{.type=CSON_TYPE_CHAR, key="id", offset=0}
对于结构体的每个成员,都进行数据模型的定义,就可以得到一个完整的结构体数据模型,CSON会根据这个模型,进行解析。
因为是通过直接写内存的方式,所以在写不同类型的量到内存中时,会多次用到强制转型,导致CSON中赋值的代码都类似于:
*(int *)((int)obj + model[i].offset) = (int)csonDecodeNumber(json, model[i].key);
当然,上面说到的数据模型,只适用于基本数据类型的数据,对于子结构体,链表,数组等,需要对数据模型的定义进行扩充,有兴趣的朋友可以直接阅读CSON源码。
CSON使用实例
声明结构体:
/** 项目结构体 */
struct project
{
int id;
char *name;
};
/** 仓库结构体 */
struct hub
{
int id;
char *user;
struct project *cson;
};
定义数据模型:
对每一个需要使用cson的结构体,都需要定义相对应的数据模型
/** 项目结构体数据模型 */
CsonModel projectModel[] =
{
CSON_MODEL_OBJ(struct project),
CSON_MODEL_INT(struct project, id),
CSON_MODEL_STRING(struct project, name),
};
/** 仓库结构体数据模型 */
CsonModel hubModel[] =
{
CSON_MODEL_OBJ(struct hub),
CSON_MODEL_INT(struct hub, id),
CSON_MODEL_STRING(struct hub, user),
CSON_MODEL_STRUCT(struct hub, cson, projectModel, sizeof(projectModel)/sizeof(CsonModel))
};
使用CSON解析:
只需要定义好数据模型,就可以使用CSON读json进行序列化和反序列化
void csonDemo(void)
{
char *jsonDemo = "{\"id\": 1, \"user\": \"Letter\", \"cson\": {\"id\": 2, \"name\": \"cson\"}}";
/** 解析json */
struct hub *pHub = csonDecode(jsonDemo, hubModel, sizeof(hubModel)/sizeof(CsonModel));
printf("hub: id: %d, user: %s, project id: %d, project name: %s\r\n",
pHub->id, pHub->user, pHub->cson->id, pHub->cson->name);
/** 序列化对象 */
char *formatJson = csonEncodeFormatted(pHub, hubModel, sizeof(hubModel)/sizeof(CsonModel));
printf("format json: %s\r\n", formatJson);
/** 释放结构体对象 */
csonFree(pHub, hubModel, sizeof(hubModel)/sizeof(CsonModel));
/** 释放序列化生成的json字符串 */
csonFreeJson(formatJson);
}
运行结果:
hub: id: 1, user: Letter, project id: 2, project name: cson
format json: {
"id": 1,
"user": "Letter",
"cson": {
"id": 2,
"name": "cson"
}
}
可以看到,无论是解析json,还是序列化结构体到json,在使用CSON的情况下,都只需要一行代码就可以解决,同样的操作,在使用原生cJSON的情况下,你可能需要多次判断,解析元素。
CSON地址
以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !