任务
(2021年7月16日-7月18日)
阅读cJOSN源码,记录源码中难点,理清代码的逻辑结构。
学习记录
源码获取
因为限制原因,无法从GitHub上直接git源码,但是偶尔能访问GitHub,想通过直接复制的方式暴力获取到本地,然而又因为GitHub上有行号,直接复制会导致将行号一起复制下来。
解决方案:手机访问GitHub,复制时不包含行号,再登录CSDN发布,电脑就可以访问文章获取到源码了。
不是很重要的东西
cJSON_Version()
/*
project version 版本信息
调用cJSON_Version函数可打印此信息
*/
#define CJSON_VERSION_MAJOR 1
#define CJSON_VERSION_MINOR 7
#define CJSON_VERSION_PATCH 14
/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 14)
#error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
#endif
/*调用下面函数就可以输出版本信息*/
CJSON_PUBLIC(const char*) cJSON_Version(void)
{
static char version[15];
sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
return version;
}
使用方法
printf("Version: %s\n", cJSON_Version());
嵌套深度
/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
* This is to prevent stack overflows. */
#ifndef CJSON_NESTING_LIMIT
#define CJSON_NESTING_LIMIT 1000 //嵌套深度 防止堆栈溢出做出的保护措施
#endif
cJOSN结构体
首先,它不是将一整段JSON数据抽象出来,而是将其中的一条JSON数据抽象出来,也就是一个键值对,用下面的结构体 strcut cJSON 来表示,其中用来存放值的成员列表
其次,一段完整的JSON数据中由很多键值对组成,并且涉及到键值对的查找、删除、添加,
所以使用链表来存储整段JSON数据
最后,因为JSON数据支持嵌套,所以一个键值对的值会是一个新的JSON数据对象(一条新的链表),也有可能是一个数组,方便起见,在cJSON中,数组也表示为一个数组对象,用链表存储,所以: 在键值对结构体中,当该键值对的值是一个嵌套的JSON数据或者一个数组时,由child指针指向该条新链表。
cJOSN的核心采用了双向链表,通过next和prev寻找目标节点,实现添加和删除操作。
而且加入了*child子节点指针,可以实现更加复杂的层级结构。
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next; //next指针:指向下一个键值对
struct cJSON *prev; //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; //包含嵌套的数据中,child指针指向值中形成的新链表
/* The type of the item, as above. */
int type; //用于表示该键值对中值的类型 在/* cJSON Types: */ 中取值
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring; //如果键值类型(type)是字符串,则将该指针指向键值
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint; //如果键值类型(type)是整数,则将该指针指向键值
/* The item's number, if type==cJSON_Number */
double valuedouble; //如果键值类型(type)是浮点数,则将该指针指向键值
/* 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;
内存管理函数
在 cJSON项目里面,是留有 cJSON_InitHooks();外部引用内存管理函数的 API接口的,通过结构体 struct cJSON_Hooks跟内部调用的内存分配挂钩
一般情况是默认不调用 cJSON_InitHooks();函数的,因此,其内存分配管理处于默认状态,使用的是以下标准内存分配函数internal_XXX()
如此一来,假设我们在系统上跑了 FreeRTOS(或者其他 RTOS),那么,在默认情况下,如果使用其标准内存分配函数,这样,对于多线程来讲是不安全的,所以,可以利用该函数重新把内存分配函数定义调用;例如在 FreeRTOS中:
cJSON_Hooks cJSON_mem;
cJSON_mem.malloc_fn = pvPortMalloc;
cJSON_mem.free_fn = vPortFree;
cJSON_InitHooks(&cJSON_mem);
通过该钩子函数,把 cJSON内部调用的内存分配处理,更换为线程安全的 pvPortMalloc();和 vPortFree();函数。
cJSON_Hooks结构体
/*cJSON支持自定义内存管理函数,其结构体定义如下所示:*/
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;
cJSON_InitHooks()函数
初始化hooks, 不同系统内存分配函数稍有差别,通过cJSON_InitHooks实现兼容。
此结构体内有两个函数指针,通过名字我们可以知道这就是内存申请和释放的函数指针。通过编写自定义的内存管理函数对该结构体赋值,调用cJSON_InitHooks函数对global_hooks全局变量赋值
CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
{
if (hooks == NULL)
{
/* Reset hooks */
global_hooks.allocate = malloc;
global_hooks.deallocate = free;
global_hooks.reallocate = realloc;
return;
}
global_hooks.allocate = malloc;
if (hooks->malloc_fn != NULL)
{
global_hooks.allocate = hooks->malloc_fn;
}
global_hooks.deallocate = free;
if (hooks->free_fn != NULL)
{
global_hooks.deallocate = hooks->free_fn;
}
/* use realloc only if both free and malloc are used */
global_hooks.reallocate = NULL;
if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
{
global_hooks.reallocate = realloc;
}
}
使用方法
cJSON_Hooks* hooks=NULL;
cJSON_InitHooks(hooks);
internal_hooks和global_hooks
global_hooks
全局变量类型及初始化如下:
typedef struct internal_hooks
{
void *(CJSON_CDECL *allocate)(size_t size);
void (CJSON_CDECL *deallocate)(void *pointer);
void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
} internal_hooks;
static internal_hooks global_hooks = {
internal_malloc,
internal_free,
internal_realloc };
internal_hooks结构体成员也是内存管理的函数指针,不同于cJSON_InitHooks,它多了一个reallocate成员。在global_hooks全局变量中,其成员通过宏定义全部初始化为默认的内存管理函数,即malloc、free、realloc。internal_realloc无法通过cJSON_InitHooks函数重新定义。
接下来cJSON库会调用global_hooks全局变量里面的内存管理函数。
举例
在cJSON_ParseWithLengthOpts
函数中,使用item = cJSON_New_Item(&global_hooks);
语句可以创建一个节点指针(21行)
/* Parse an object - create a new root, and populate. */
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
{
parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
cJSON *item = NULL;
/* reset error position */
global_error.json = NULL;
global_error.position = 0;
if (value == NULL || 0 == buffer_length)
{
goto fail;
}
buffer.content = (const unsigned char*)value;
buffer.length = buffer_length;
buffer.offset = 0;
buffer.hooks = global_hooks;
item = cJSON_New_Item(&global_hooks); //创建一个节点指针
if (item == NULL) /* memory fail */
{
goto fail;
}
if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
{
/* parse failure. ep is set. */
goto fail;
}
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated)
{
buffer_skip_whitespace(&buffer);
if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
{
goto fail;
}
}
if (return_parse_end)
{
*return_parse_end = (const char*)buffer_at_offset(&buffer);
}
return item;
fail:
if (item != NULL)
{
cJSON_Delete(item);
}
if (value != NULL)
{
error local_error;
local_error.json = (const unsigned char*)value;
local_error.position = 0;
if (buffer.offset < buffer.length)
{
local_error.position = buffer.offset;
}
else if (buffer.length > 0)
{
local_error.position = buffer.length - 1;
}
if (return_parse_end != NULL)
{
*return_parse_end = (const char*)local_error.json + local_error.position;
}
global_error = local_error;
}
return NULL;
}
其中cJSON_New_Item
原型函数如下:
/* Internal constructor. */
static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
{
cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
if (node)
{
memset(node, '\0', sizeof(cJSON)); //将node初始化为0
}
return node;
}
由此可知,cJSON_New_Item
通过调用global_hooks
中的allocate
成员申请内存。
cJSON_Parse类函数
数据解析
cJSON_Parse()
序列化json字符串
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); //将cJSON字符串格式化为cJSON链表,并返回指向此链表的指针。
使用方法
char * string = "{\"name\":\"xxx\", \"name2\":\"xxx2\"}";
cJSON * root = cJSON_Parse(string);
cJSON_ParseWithLength()
和 cJSON_Parse没有太大区别,其内部也要计算json字符串的长度
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value,
size_t buffer_length);
使用方法
char * string = "{\"name\":\"xxx\", \"name2\":\"xxx2\"}";
cJSON * root = cJSON_ParseWithLength(string, strlen(string)+sizeof(""));//考虑到字符串末尾的长度
cJSON_ParseWithOpts()
也是格式化json字符串return_parse_end, require_null_terminated默认传0
CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value,
const char **return_parse_end,
cJSON_bool require_null_terminated);
使用方法
char * string = "{\"name\":\"xxx\",\"name2\":\"xxx2\"\"}";
cJSON * root = cJSON_ParseWithOpts(string, 0, 0);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
printf("%s\n", cJSON_Print(root));
cJSON_ParseWithLengthOpts()
参数要求同cJSON_ParseWithOpts一致
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value,
size_t buffer_length,
const char **return_parse_end,
cJSON_bool require_null_terminated);
使用方法
char * string = "{\"name\":\"xxx\",\"name2\":\"xxx2\"}";
cJSON * root = cJSON_ParseWithLengthOpts(string, strlen(string)+sizeof(""), 0, 0);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
printf("%s\n", cJSON_Print(root));
说明
但一般来说,平常我们只需要用到 cJSON_Parse();函数来解析。同样的,在调用了 parse函数后,使用完毕需要调用 cJSON_Delete();及时释放;
整个解析过程,其核心操作函数为:
static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
原理是通过搜寻对应的配对关键符号或者关键字去调用不同的处理函数,然后配对校验,并把相应的数据插入到根结点,形成一个个相连的子节点链表。
数据获取
当调用完上面的解析函数后,返回的是根结点指针,通过这个 cJSON的结构指针,就可以利用其解析后每个节点所对应的类型,快速寻找同类型的数据,再根据提供的键(名称)来获取数据。
类型校验
cJSON_IsXXX()
类型检查函数
- cJSON_IsInvalid()
- cJSON_IsFalse()
- cJSON_IsTrue()
- cJSON_IsBool()
- cJSON_IsNull()
- cJSON_IsNumber()
- cJSON_IsString()
- cJSON_IsArray()
- cJSON_IsObject()
- cJSON_IsRaw()
/* cJSON Types: */
#define cJSON_Invalid (0) //表示不包含任何值的无效项。用cJSON_IsInvalid()函数来判断。
#define cJSON_False (1 << 0) //表示一个错误的布尔值。用cJSON_IsFalse()或cJSON_IsBool()函数来判断。
#define cJSON_True (1 << 1) //表示一个正确的布尔值。用cJSON_IsTrue()或cJSON_IsBool()函数来判断。
#define cJSON_NULL (1 << 2) //表示一个null值。用cJSON_IsNull()函数来判断。
#define cJSON_Number (1 << 3) //表示一个数字值。用cJSON_IsNumber()函数来判断。
#define cJSON_String (1 << 4) //表示一个字符串值。用cJSON_IsString()函数来判断。
#define cJSON_Array (1 << 5) //表示一个数组值。可以使用cJSON_IsArray()函数来判断。
#define cJSON_Object (1 << 6) //表示一个对象值。可以使用cJSON_IsObject()函数来判断。
#define cJSON_Raw (1 << 7) //表示存储在valuestring(cJSON结构体)中以0终止的字符数组的任何JSON。可以使用cJSON_IsRaw()函数来判断。
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); //无效
CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); //False
CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); //True
CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); //布尔值
CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); //空
CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); //数值
CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); //字符串
CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); //数组
CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); //对象
CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
//此外,还有cJSON_IsReference表示引用及cJSON_StringIsConst表示string指向的是一个常量字符串两种类型标志。
上面这些函数都是用于判断参数的类型的,因此返回值只有 true(真)和 false(假);用的比较多的是 cJSON_IsFalse()和 cJSON_IsTrue(),直接判断JSON数据包里的布尔变量。
使用方法
char * string = "{\"name\":\"xxxxxxxxx\",\"test\":\"xxx2\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}else{
cJSON *temp = cJSON_GetObjectItem(root, "name");
//简单例子
if(cJSON_IsInvalid(temp))printf("改对象是无效的\n");
if(cJSON_IsString(temp))printf("是字符串\n");
if(cJSON_IsFalse(temp))printf("是假\n");
if(cJSON_IsTrue(temp))printf("是真\n");
if(cJSON_IsBool(temp))printf("是布尔类型\n");
if(cJSON_IsNull(temp))printf("是空\n");
if(cJSON_IsNumber(temp))printf("是数字\n");
if(cJSON_IsArray(temp))printf("是数组\n");
}
cJSON_Get类函数
获取项目数
cJSON_GetArraySize()
返回数组(或对象)中的项数,就是获取数组大小/对象/项。所谓的项数,就是同一层下,有多少对键值对
/* Returns the number of items in an array (or object). */
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
使用方法
char * string = "{\"name\":\"xxxxxxxxx\",\"name2\":\"xxx2\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
{
printf("%d\n", cJSON_GetArraySize(root));//输出2,因为root下面有两对键值对
}
信息提取
cJSON_GetArrayItem()
从数组“array”中检索项目编号“index”。如果不成功,则返回NULL。
就是在json对象当前层下,可以通过索引遍历键名和键值。注意,cJSON_GetArrayItem返回的是cJSON *类型,通过查看该类型的数据结构从而正确访问键名和键值
/* Retrieve item number "index" from array "array". 从数组array中取回项目数值index
Returns NULL if unsuccessful.不成功返回空 */
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); //数组
使用方法
char * string = "{\"name\":\"xxxxxxxxx\",\"name2\":\"xxx2\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
{
for(int i=0; i<cJSON_GetArraySize(root); i++)
{
cJSON * temp = cJSON_GetArrayItem(root, i);
if(temp != NULL)printf("root[%s] = %s\n", temp->string, temp->valuestring);
}
}
/**************************************
root[name] = xxxxxxxxx
root[name2] = xxx2
**************************************/
cJSON_GetObjectItem()
如果想直接通过键名的方式获得键值,可以通过此方法。定位到想要的键名的层次之后,调用此函数即可。(注意,输入的键名是不区分大小写的,也就是说cJSON_GetObjectItem(root, “name”)和cJSON_GetObjectItem(root, “NAME”))是一样的。要是想要区分大小写,请使用cJSON_GetObjectItemCaseSensitive函数,使用方法跟cJSON_GetObjectItem一致
/* Get item "string" from object. Case insensitive. */
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); //对象
使用方法
char * string = "{\"name\":\"xxxxxxxxx\",\"name2\":\"xxx2\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}else{
cJSON * temp = cJSON_GetObjectItem(root, "name");//通过键名访问键值
printf("root[%s] = %s\n", temp->string, temp->valuestring);
temp = cJSON_GetObjectItem(root, "name2");//通过键名访问键值
printf("root[%s] = %s\n", temp->string, temp->valuestring);
}
cJSON_GetObjectItemCaseSensitive()
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); //对象(名称区分大小写)
校验类型并返回值
cJSON_GetXXXValue()
检查项目类型并返回其值
- cJSON_GetStringValue()
- cJSON_GetNumberValue()
/* Check item type and return its value */
CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); //字符串
CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); //数值
使用方法
char * string = "{\"name\":\"xxxxxxxxx\",\"test\":\"xxx2\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}else{
if(cJSON_IsString(root))printf("%s\n", cJSON_GetStringValue(root));
else printf("不是字符串类型\n");
cJSON * temp = cJSON_GetObjectItem(root, "name");
//判断该对象的值是不是字符串类型,并且通过特定函数直接获取
if(cJSON_IsString(temp))printf("%s\n", cJSON_GetStringValue(temp));
else printf("不是字符串类型\n");
// GetNumberValue函数用法也是一样的
}
错误分析
cJSON_GetErrorPtr()
/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
判断键名是否存在
cJSON_HasObjectItem()
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); //用来判断查找的键名是否存在
使用方法
char * string = "{\"name\":\"xxxxxxxxx\",\"test\":\"xxx2\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}else{
if(!cJSON_HasObjectItem(root, "name2"))printf("该键名不存在\n");
if(cJSON_HasObjectItem(root, "name")){
cJSON * temp = cJSON_GetObjectItem(root, "name");//通过键名访问键值
printf("root[%s] = %s\n", temp->string, temp->valuestring);
}
}
数据打印
cJSON_Print()
格式化打印json对象
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); //格式化打印json对象
cJSON_PrintUnformatted()
非格式化打印json对象
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
cJSON_PrintBuffered()
使用缓冲策略将cJSON实体渲染为文本。prebuffer是对最终大小的猜测。猜得好会减少再分配(意思就是最好渲染成文本的长度刚好就是prebuffer的长度,如果不够,会一直触发再分配内存函数直到满足为止,这就消耗太多时间)。fmt=0表示未格式化,=1表示格式化
CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
使用方法
char * string = "{\"name\":\"xxxxxxxxxxxxxxx\",\"name2\":\"xxx2\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}else{
//为了只需要一次性分配内存,prebuffer大一点比较好,但也不要太大,这个看情况估计,下面的都是刚刚好
printf("%s\n", cJSON_PrintBuffered(root, strlen(string), 0));//非格式化输出
printf("%s\n", cJSON_PrintBuffered(root, strlen(string), 1)); //格式化输出
}
cJSON_PrintPreallocated()
注意:cJSON并不总是100%准确地估计它将使用多少内存,所以为了安全起见,要比实际需要多分配5个字节。而且使用cJSON_PrintPreallocated的话,缓冲区就全部由调用者控制,内存的分配与回收调用者需要十分清楚
CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
cJSON_Delete类函数
当调用 cJSON_Delete();函数后,会通过 while循环一直从当前节点删除释放其后面的节点,直至到尾部结点 null节点为止;因此,在应用中,一般都是传入主链表的头结点来释放整个 JSON数据包。
- cJSON_Delete() //释放位于堆中的cJSON结构体内存。
- cJSON_DeleteItemFromArray()
- cJSON_DeleteItemFromObject()
- cJSON_DeleteItemFromObjectCaseSensitive()
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); //释放位于堆中的cJSON结构体内存。
/*从数组/对象中移除项,与cJSON_DetachItemFromArrayX使用相同*/
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
使用方法
char * string = "{\"name\":\"xxxxxxxxx\",\"name2\":\"xxx2\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}else{
printf("%s\n", cJSON_PrintUnformatted(root));
cJSON_Delete(root);//root指向的地址没有改变,但是无法使用那块内存,所以用给root赋值NULL好判断
root = NULL;
if(root==NULL)printf("删除成功\n");
else printf("%s\n", cJSON_PrintUnformatted(root));
}
cJSON_DetachItemX分离类函数
使用方法和cJSON_Delete类函数一样
- cJSON_DetachItemViaPointer()
- cJSON_DetachItemFromArray()
- cJSON_DetachItemFromObject()
- cJSON_DetachItemFromObjectCaseSensitive()
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); //cJSON_DetachItemViaPointer 通过指针分离键值对
/*从数组/对象中分离项,与cJSON_DeleteItemFromX使用相同*/
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
cJSON_AddX添加类函数
cJSON_AddItemToXXX()
- cJSON_AddItemToArray()
- cJSON_AddItemToObject()
- cJSON_AddItemToObjectCS()
将cJSON *类型的变量添加到json对象中
对于cJSON_AddItemToObjectCS函数,当字符串肯定是const(即一个等同于literal),并且肯定会在cJSON对象中保留下来时,使用此选项。注意:使用此函数时,请确保在使用前始终检查(item->type&cJSON_StringIsConst)是否为零
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
* WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
* writing to `item->string` */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
使用方法
char * string = "{\"key\":\"value\"}";
int arr[] = {1,2,3,4};
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}else{
cJSON * temp = cJSON_CreateIntArray(arr, 4);
// temp->string = "arr_key";//设置键名
// cJSON_AddItemToArray(root, temp);//将该键值对加入到root对象
//也可以通过别的方式设置键名
cJSON_AddItemToObject(root, "arr_key", temp);
printf("%s\n", cJSON_Print(root));
}
cJSON_AddItemReferenceToXXX()
- cJSON_AddItemReferenceToArray()
- cJSON_AddItemReferenceToObject()
如果想要将引用的 cJSON*变量 追加到json对象里面,上面两个函数可以解决该问题
/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
使用方法
char * string = "{\"key\":\"value\"}";
int arr[] = {1,2,3};
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}else{
// cJSON * temp = cJSON_CreateArrayReference(arr);
cJSON * temp = cJSON_CreateArrayReference(NULL);//相当于创建一个空数组,由于是引用,无法用cJSON_Delete删除
// printf("%s\n", cJSON_Print(temp));
cJSON_AddItemToObject(root, "arr_key", temp);//追加到root对象里面
printf("%s\n", cJSON_Print(root));
}
cJSON_AddXXXToObject()
- cJSON_AddNullToObject()
- cJSON_AddTrueToObject()
- cJSON_AddFalseToObject()
- cJSON_AddBoolToObject()
- cJSON_AddNumberToObject()
- cJSON_AddStringToObject()
- cJSON_AddRawToObject()
- cJSON_AddObjectToObject()
- cJSON_AddArrayToObject()
以下是给json对象添加数组或对象或字符串等所需要的API函数
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
使用方法
char * string = "{\"name\":\"xxxxxxxxx\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}else{
printf("添加键值对\n");
cJSON_AddNullToObject(root, "isnull");
// cJSON_AddTrueToObject(root, "istrue");//可能会莫名报错(执行后cJSON_Print(root)打印为空),原因未知
// cJSON_AddFalseToObject(root, "isfalse");//可能会莫名报错(执行后cJSON_Print(root)打印为空),原因未知
// cJSON_AddBoolToObject(root, "bool", 0);//可能会莫名报错(执行后cJSON_Print(root)打印为空),原因未知
cJSON_AddStringToObject(root, "string_name", "test");
cJSON_AddNumberToObject(root, "num", 123456);
cJSON_AddObjectToObject(root, "new_obj");
cJSON_AddRawToObject(root, "raw_obj", "{\"age\":\"110\"}");//所谓原生json就是还是字符串形式,需要格式化后才可使用
cJSON_AddArrayToObject(root, "arr");
printf("%s\n", cJSON_Print(root));
}
cJSON_CreateX创建类函数
cJSON_CreateXXX()
- cJSON_CreateNull()
- cJSON_CreateTrue()
- cJSON_CreateFalse()
- cJSON_CreateBool()
- cJSON_CreateNumber()
- cJSON_CreateString()
- cJSON_CreateRaw()
- cJSON_CreateArray()
- cJSON_CreateObject()
这些调用函数将创建相应类型的cJSON项
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
/* raw json */
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
使用方法
char * string = "{\"name\":\"xxxxxxxxx\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}else{
printf("添加键值对\n");
cJSON_AddItemToObject(root, "num", cJSON_CreateNumber(100));
// cJSON_AddItemToObject(root, "is_null", cJSON_CreateNull());//可能会莫名报错(执行后cJSON_Print(root)打印为空),原因未知
// cJSON_AddItemToObject(root, "is_true", cJSON_CreateTrue());//可能会莫名报错(执行后cJSON_Print(root)打印为空),原因未知
// cJSON_AddItemToObject(root, "is_false", cJSON_CreateFalse());//可能会莫名报错(执行后cJSON_Print(root)打印为空),原因未知
cJSON_AddItemToObject(root, "str", cJSON_CreateString("string"));
cJSON_AddItemToObject(root, "raw_json", cJSON_CreateRaw("{\"age\":\"111\"}"));
cJSON_AddItemToObject(root, "arr", cJSON_CreateArray());
cJSON_AddItemToObject(root, "new_obj", cJSON_CreateObject());
printf("%s\n", cJSON_Print(root));
}
cJSON_CreateXXXReference()
- cJSON_CreateStringReference()
- cJSON_CreateObjectReference()
- cJSON_CreateArrayReference()
创建一个字符串的引用(说白了就是常量字符串变量),但不可通过cJSON_Delete释放该引用( 源代码里面对引用类型的数据直接跳过,不做free处理 )。下面两个相同。
CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
使用方法
char * string = "{\"key\":\"value\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
{
cJSON * temp = cJSON_CreateStringReference("name");//只能获得键值,不能获得键名
printf("%s : %s\n", temp->string, temp->valuestring);
cJSON_Delete(temp);//无法通过引用删除,所以无效
printf("%s : %s\n", temp->string, temp->valuestring);//还可以输出name
}
cJSON_CreateXXXArray()
- cJSON_CreateIntArray()
- cJSON_CreateFloatArray()
- cJSON_CreateDoubleArray()
- cJSON_CreateStringArray()
这四个都是创建数组的方式,区别是数组的值类型不同
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
使用方法
char * string = "{\"key\":\"value\"}";
int arr[] = {1,2,3,4};
cJSON * root = cJSON_Parse(string);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
{
cJSON * temp = cJSON_CreateIntArray(arr, 4);
temp->string = "arr_key";//设置键名
cJSON_AddItemToArray(root, temp);//将该键值对加入到root对象
printf("%s\n", cJSON_Print(root));
/*
类似的,cJSON_CreateFloatArray、cJSON_CreateDoubleArray、cJSON_CreateStringArray函数的用法与上面的一致
*/
}
cJSON_InsertItemX插入函数
cJSON_InsertItemInArray()
CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
使用方法
char * string = "{\"key\":\"value\", \"key2\":[{\"index\":\"value\"},{\"index2\":\"value2\"}]}";
cJSON * root = cJSON_Parse(string);
if(root==NULL){
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
{
printf("%s\n\n", cJSON_Print(root));
cJSON * temp = cJSON_GetObjectItem(root, "key2");//获得该键名的json对象
cJSON * obj = cJSON_CreateObject();
cJSON_AddStringToObject(obj, "name", "xxx");
cJSON_InsertItemInArray(temp, 0, obj);//在数组key2第一个位置插入json对象obj
printf("%s\n\n", cJSON_Print(root));
}
cJSON_ReplaceItemX替换函数
- cJSON_ReplaceItemViaPointer()
- cJSON_ReplaceItemInArray()
- cJSON_ReplaceItemInObject()
- cJSON_ReplaceItemInObjectCaseSensitive()
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); //使用指针替代Item
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); //替换数组中的Item
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); //将object中键值名为string的键值替换成cJSON类型的newitem
CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); //大小写敏感的替换Object的Item
char * string = "{\"key\":\"value\", \"key2\":[{\"index\":\"value\"},{\"index2\":\"value2\"}]}";
cJSON * root = cJSON_Parse(string);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
{
printf("%s\n\n", cJSON_Print(root));
cJSON * temp = cJSON_GetObjectItem(root, "key2");//获得该键名的json对象
cJSON * obj = cJSON_CreateObject();
cJSON_AddStringToObject(obj, "name", "xxx");
cJSON_InsertItemInArray(temp, 0, obj);//在数组key2第一个位置插入json对象obj
printf("%s\n\n", cJSON_Print(root));
cJSON * obj2 = cJSON_CreateObject();
cJSON_AddStringToObject(obj2, "name2", "xxx2");
cJSON_ReplaceItemViaPointer(temp, obj, obj2);//在temp对象中,将obj替换成obj2(只要可以找到该对象,就可以替换)
printf("%s\n", cJSON_Print(root));
cJSON_ReplaceItemInObject(root,"key",cJSON_CreateString("hello"));//用于代替root对象中key元组的值
// 同理,cJSON_ReplaceItemInArray、cJSON_ReplaceItemInObjectCaseSensitive、cJSON_ReplaceItemInObject用法同上,注意参数要求
printf("%s\n", cJSON_Print(root));
}
cJSON_Duplicate复制函数
该函数复制相同的cJSON对象,其中recurse若为1,则表示全部复制(包括深层次的json对象)
CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); //复制cJSON结构体
使用方法
char * string = "{\"key\":\"value\", \"key2\":[{\"index\":\"value\"},{\"index2\":\"value2\"}]}";
cJSON * root = cJSON_Parse(string);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
{
printf("%s\n\n", cJSON_Print(root));
// cJSON *temp = cJSON_Duplicate(root, 0);
cJSON *temp = cJSON_Duplicate(root, 1);
printf("%s\n", cJSON_Print(temp));
}
cJSON_Compare比较函数
递归地比较两个cJSON项是否相等。如果a或b为空或无效,则它们将被视为不相等。
区分大小写确定对象键是区分大小写(1)还是不区分大小写(0)
CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); //比较两个cJSON结构体
使用方法
char * string = "{\"key\":\"value\", \"key2\":[{\"index\":\"value\"},{\"index2\":\"value2\"}]}";
cJSON * root = cJSON_Parse(string);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
{
printf("%s\n\n", cJSON_Print(root));
cJSON *temp = cJSON_Duplicate(root, 1);//复制root结构体给temp
int bools = cJSON_Compare(root, temp, 1);//区分大小写
printf("%d\n", bools);
}
cJSON_Minify()压缩函数
压缩字符串,删除字符串中的空白符
CJSON_PUBLIC(void) cJSON_Minify(char *json); //将格式化的字符串压缩
使用方法
char * string = "{\"key\":\"v a l u e\"}";
cJSON * root = cJSON_Parse(string);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
{
printf("%s\n\n", cJSON_Print(root));
cJSON* temp = cJSON_GetObjectItem(root, "key");
cJSON_Minify(temp->valuestring);//压缩字符串
printf("%s\n", cJSON_Print(root));
}
cJSON_SetXXX()
cJSON_SetNumberHelper()
设置cJSON的number类型的值
CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
cJSON_SetValuestring()
CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
cJSON_malloc()
CJSON_PUBLIC(void *) cJSON_malloc(size_t size); //cJSON的malloc函数,调用malloc函数
cJSON_free()
CJSON_PUBLIC(void) cJSON_free(void *object); //cJSON的free函数,调用free函数
一般来说,cJSON_malloc();很少用,因为 cJSON的数据处理 API函数都默认会自动分配内存;而 cJSON_free();则更多的是用来 free cJSON格式化出来的数据(即调用 print类的 API接口)。
宏定义函数
cJSON_SetIntValue()
#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
cJSON_SetNumberValue()
#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
cJSON_ArrayForEach()
用于迭代数组或对象的宏
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
使用方法
char * string = "{\"key\":\"value\", \"key2\":[{\"index\":\"value\"},{\"index2\":\"value2\"}]}";
cJSON * root = cJSON_Parse(string);
if(root==NULL)
{
printf("Error_content : %s\n", cJSON_GetErrorPtr());
}
else
{
cJSON * temp = cJSON_GetObjectItem(root, "key2");
cJSON * element;
cJSON_ArrayForEach(element, root)
{
printf("%s\n", cJSON_Print(element));//迭代遍历json对象(只对当前层有效)
}
cJSON_ArrayForEach(element, temp)
{
printf("%s\n", cJSON_Print(element));//迭代遍历数组
}
printf("%s\n", cJSON_Print(temp));
}
数组类型
1)cJSON_CreateArray()函数用于创建一个空数组,或者使用cJSON_CreateArrayReference()函数创建一个数组引用。需要注意的是,数组引用中的实际数据不会被cJSON_Delete()函数释放。
2)cJSON_AddItemToArray()函数用于向数组中添加条目,或者使用cJSON_AddItemReferenceToArray()函数添加其他的条目(item)、数组(array)、字符串(string)的引用到当前数组中。
3)cJSON_InsertItemInArray()函数用于向数组中插入一个条目。该函数需要提供插入索引的位置,插入指定位置之后,该位置之后的所有条目需要依次向右移动。
4)cJSON_DetachItemFromArray()函数用于从给定的索引处取出一个条目。需要注意的是,记得将该函数取出的返回值赋予一个指针再使用,否则会出现内存泄露。
5)cJSON_DeleteItemFromArray()函数用于删除一个条目。该函数与cJSON_DetachItemFromArray()类似,不同的是该函数是通过调用cJSON_Delete()函数删除分离的条目。
6)cJSON_ReplaceItemInArray()函数用于替换一个给定索引位置的条目,类似的cJSON_ReplaceItemViaPointer()函数用于替换指针指向的条目。
7)cJSON_GetArraySize()函数用于获取数组大小。
8)cJSON_GetArrayItem()函数用于获取指定索引上的元素。
9)其他。
对象类型
1)cJSON_CreateObject()函数用于创建一个空对象,cJSON_CreateObjectReference()函数用于创建一个对象引用。
2)cJSON_AddItemToObject()函数用于向对象中添加一个条目。cJSON_AddItemToObjectCS()函数用于向名称(name)为常量(constant)或引用(reference)的对象中添加一个条目,类似的添加函数还有cJSON_AddStringToObject(), cJSON_AddArrayToObject(), cJSON_AddNumberToObject()等。
3)cJSON_DetachItemFromObjectCaseSensitive()函数用于从对象中取出一个条目。
4)cJSON_DeleteItemFromObjectCaseSensitive()函数删除对象中的一个条目。
5)cJSON_ReplaceItemInObjectCaseSensitive()及cJSON_ReplaceItemViaPointer()函数可用于替换对象中某个条目。
6)cJSON_GetArraySize()函数用于获取对象的大小。需要注意的是,对象内部是以数组形式存储的。
7)cJSON_GetObjectItemCaseSensitive()函数用来访问对象中的条目。
8)cJSON_ArrayForEach宏可用于遍历对象。
9)cJSON_AddNullToObject()函数用于快速创建一个新的空条目到对象中。
10)其他。
注意
因为cJSON中的数组的底层是通过链表实现的,所以通过索引来遍历数组的话效率会很低(O(n^2)),因此cJSON提供了宏cJSON_ArrayForEach可以实现时间复杂度为O(n)的遍历数组。
/* Macro for iterating over an array or object */
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)