一:cJSON
cJSON是一个使用C语言编写的JSON数据解析器,具有超轻便,可移植,单文件的特点,使用MIT开源协议。
cJSON项目托管在Github上,仓库地址如下:
https://github.com/DaveGamble/cJSON
git clone https://github.com/DaveGamble/cJSON.git
从Github拉取cJSON源码后,文件非常多,但是其中cJSON的源码文件只有两个:
/
cJSON.h cJSON.c
使用的时候,只需要将这两个文件复制到工程目录,然后包含头文件cJSON.h即可,如下:
#include "cJSON.h"
二: cJSON数据结构和设计思想
cJSON使用cJSON结构体来表示一个JSON数据,定义在cJSON.h中,源码如下:
typedef struct cJSON { /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ struct cJSON *next; struct cJSON *prev; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ struct cJSON *child; /* The type of the item, as above. */ int type; /* The item's string, if type==cJSON_String and type == cJSON_Raw */ char *valuestring; /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ int valueint; /* The item's number, if type==cJSON_Number */ double valuedouble; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ char *string; } cJSON;
strcut cJSON 来表示,其中用来存放值的成员列表如下:
String:用于表示该键值对的名称;
type:用于表示该键值对中值的类型;
valuestring:如果键值类型(type)是字符串,则将该指针指向键值;
valueint:如果键值类型(type)是整数,则将该指针指向键值;
valuedouble:如果键值类型(type)是浮点数,则将该指针指向键值;
一段完整的JSON数据中由很多键值对组成,并且涉及到键值对的查找、删除、添加,所以使用链表来存储整段JSON数据,
next指针:指向下一个键值对
prev指针指向上一个键值对
因为JSON数据支持嵌套,所以一个键值对的值会是一个新的JSON数据对象(一条新的链表),也有可能是一个数组,方便起见,在cJSON中,数组也表示为一个数组对象,用链表存储
当该键值对的值是一个嵌套的JSON数据或者一个数组时,由child指针指向该条新链表。
三:JSON数据封装
头指针:指向链表头结点的指针;
头结点:不存放有效数据,方便链表操作;
首节点:第一个存放有效数据的节点;
尾节点:最后一个存放有效数据的节点;
1,创建头指针
cJSON* cjson_test = NULL;
2,创建头节点,并将头指针指向头结点
cjson_test = cJSON_CreateObject();
3,向链表中添加节点
cJSON_AddNullToObject(cJSON * const object, const char * const name); cJSON_AddTrueToObject(cJSON * const object, const char * const name); cJSON_AddFalseToObject(cJSON * const object, const char * const name); cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); cJSON_AddObjectToObject(cJSON * const object, const char * const name); cJSON_AddArrayToObject(cJSON * const object, const char * const name);
4,输出json数据
(char *) cJSON_Print(const cJSON *item);
5,具体事例
#include <stdio.h> #include "cJSON.h" int main(void) { cJSON* cjson_test = NULL; cJSON* cjson_address = NULL; cJSON* cjson_skill = NULL; char* str = NULL; /* 创建一个JSON数据对象(链表头结点) */ cjson_test = cJSON_CreateObject(); /* 添加一条字符串类型的JSON数据(添加一个链表节点) */ cJSON_AddStringToObject(cjson_test, "name", "mculover666"); /* 添加一条整数类型的JSON数据(添加一个链表节点) */ cJSON_AddNumberToObject(cjson_test, "age", 22); /* 添加一条浮点类型的JSON数据(添加一个链表节点) */ cJSON_AddNumberToObject(cjson_test, "weight", 55.5); /* 添加一个嵌套的JSON数据(添加一个链表节点) */ cjson_address = cJSON_CreateObject(); cJSON_AddStringToObject(cjson_address, "country", "China"); cJSON_AddNumberToObject(cjson_address, "zip-code", 111111); cJSON_AddItemToObject(cjson_test, "address", cjson_address); /* 添加一个数组类型的JSON数据(添加一个链表节点) */ cjson_skill = cJSON_CreateArray(); cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" )); cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java" )); cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" )); cJSON_AddItemToObject(cjson_test, "skill", cjson_skill); /* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */ cJSON_AddFalseToObject(cjson_test, "student"); /* 打印JSON对象(整条链表)的所有数据 */ str = cJSON_Print(cjson_test); printf("%s\n", str); return 0; }
输出结果:
四:cJSON数据解析
1,创建链表头指针
cJSON* cjson_test = NULL;
2,解析整段JSON数据,并将链表头结点地址返回,赋值给头指针:
解析整段数据使用的API只有一个:
(cJSON *) cJSON_Parse(const char *value);
3,根据键值对的名称从链表中取出对应的值,返回该键值对(链表节点)的地址
(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
4,如果JSON数据的值是数组,使用下面的两个API提取数据:
(int) cJSON_GetArraySize(const cJSON *array); (cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
5,事例
#include <stdio.h> #include "cJSON.h" char *message = int main(void) { cJSON* cjson_test = NULL; cJSON* cjson_name = NULL; cJSON* cjson_age = NULL; cJSON* cjson_weight = NULL; cJSON* cjson_address = NULL; cJSON* cjson_address_country = NULL; cJSON* cjson_address_zipcode = NULL; cJSON* cjson_skill = NULL; cJSON* cjson_student = NULL; int skill_array_size = 0, i = 0; cJSON* cjson_skill_item = NULL; /* 解析整段JSO数据 */ cjson_test = cJSON_Parse(message); if(cjson_test == NULL) { printf("parse fail.\n"); return -1; } /* 依次根据名称提取JSON数据(键值对) */ cjson_name = cJSON_GetObjectItem(cjson_test, "name"); cjson_age = cJSON_GetObjectItem(cjson_test, "age"); cjson_weight = cJSON_GetObjectItem(cjson_test, "weight"); printf("name: %s\n", cjson_name->valuestring); printf("age:%d\n", cjson_age->valueint); printf("weight:%.1f\n", cjson_weight->valuedouble); /* 解析嵌套json数据 */ cjson_address = cJSON_GetObjectItem(cjson_test, "address"); cjson_address_country = cJSON_GetObjectItem(cjson_address, "country"); cjson_address_zipcode = cJSON_GetObjectItem(cjson_address, "zip-code"); printf("address-country:%s\naddress-zipcode:%d\n", cjson_address_country->valuestring, cjson_address_zipcode->valueint); /* 解析数组 */ cjson_skill = cJSON_GetObjectItem(cjson_test, "skill"); skill_array_size = cJSON_GetArraySize(cjson_skill); printf("skill:["); for(i = 0; i < skill_array_size; i++) { cjson_skill_item = cJSON_GetArrayItem(cjson_skill, i); printf("%s,", cjson_skill_item->valuestring); } printf("\b]\n"); /* 解析布尔型数据 */ cjson_student = cJSON_GetObjectItem(cjson_test, "student"); if(cjson_student->valueint == 0) { printf("student: false\n"); } else { printf("student:error\n"); } return 0; }
结果:
五:cJSON使用过程中的内存问题
1,内存及时释放
cJSON的所有操作都是基于链表的,所以cJSON在使用过程中大量的使用malloc从堆中分配动态内存的,所以在使用完之后,应当及时调用下面的函数,清空cJSON指针所指向的内存,该函数也可用于删除某一条数据:
(void) cJSON_Delete(cJSON *item);
2,内存钩子
cJSON在支持自定义malloc函数和free函数,方法如下:
1,使用cJSON_Hooks来连接自定义malloc函数和free函数:
typedef struct cJSON_Hooks { /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ void *(CJSON_CDECL *malloc_fn)(size_t sz); void (CJSON_CDECL *free_fn)(void *ptr); } cJSON_Hooks;
2,初始化钩子cJSON_Hooks
(void) cJSON_InitHooks(cJSON_Hooks* hooks);