首先看下cJSON数据结构体(摘自源代码,注释的英文使用机器翻译)
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem
下一步/上一步允许您遍历数组/对象链。或者,使用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 如果type==cJSON_string且type==cJSON_Raw,则为项的字符串*/
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead
不赞成写入valueint, 请改用cJSON_SetNumberValue*/
int valueint;
/* The item's number, if type==cJSON_Number
项目编号,如果类型==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;
根据注释里面的成员变量作用可大致了解:
- next:指向当前json结构的子级json
- pre:指向当前json结构的父级json
- child:指向相邻的兄弟json对象
- type:指明存储数据的数据类型,用来判断的
- valuestring:存的值是char类型,通过指针指向存储的字符串
- valueint:存的值是int类型
- valuedouble:存的值是double类型
- string:一般json里面分键名和键值,这个应该是存储键名
了解主要的存储结构后,看一下如何初始化一个json结构。
使用cJSON_CreateObject函数初始化一个json结构
cJSON *root = cJSON_CreateObject();
那么调用cJSON_CreateObject函数时是如何初始化的?
看源代码(只截取关键部分):
/* cJSON Types: */
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define CJSON_STDCALL __stdcall
#define CJSON_PUBLIC(type) type CJSON_STDCALL
//internal_hooks简单说就是适配各类型系统的内存分配结构体,比C带的内存分配函数适应性更好
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;
#if defined(_MSC_VER)
/* work around MSVC error C2322: '...' address of dllimport '...' is not static
解决MSVC错误C2322:“…”dllimport“…”的地址不是静态的
关于CJSON_CDECL ,在cJSON.h里面已经定义
#define CJSON_CDECL __cdecl
*/
static void * CJSON_CDECL internal_malloc(size_t size)
{
return malloc(size);//分配好后返回地址
}
static void CJSON_CDECL internal_free(void *pointer)
{
free(pointer);
}
static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
{
return realloc(pointer, size);
}
#else
#define internal_malloc malloc
#define internal_free free
#define internal_realloc realloc
#endif
/*
global_hooks 主要解决内存分配问题
*/
static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
/*
CJSON_PUBLIC(cJSON *) 是有宏定义进行展开的,会展开为
cJSON * __stdcall
关于__stdcall 的用法可在这里查看:https://blog.csdn.net/qq_44647223/article/details/113389905
*/
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
{
cJSON *item = cJSON_New_Item(&global_hooks);//获得基本的初始化地址
if (item)
{
item->type = cJSON_Object;//确定该json的值类型为对象,其他类型如整型,字符串等都通过define进行标注
}
return 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));//初始值
}
return node;//返回地址
}
说明函数调用顺序:
- cJSON_CreateObject -> cJSON_New_Item -> C语言内存分配函数
对于新创建json对象,如何追加新键名跟键值?
具体调用:
/*
参数:
json对象,类型是cJSON *
键名,类型char *
键值,类型char *
*/
cJSON_AddStringToObject(root, "type", "rect");
实现功能的源代码(只截取关键部分):
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
{
cJSON *string_item = cJSON_CreateString(string);//创建字符串
//添加成功,返回地址
if (add_item_to_object(object, name, string_item, &global_hooks, false))//
{
return string_item;
}
//添加失败,回收分配除去的空间并返回NULL
cJSON_Delete(string_item);
return NULL;
}
//顾名思义,创建字符串
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
{
cJSON *item = cJSON_New_Item(&global_hooks);
if(item)
{
item->type = cJSON_String;//标记数据类型
//这里其实就是通过global_hooks将string的值拷贝到item->valuestrings上
item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
if(!item->valuestring)//如果为NULL则失败
{
cJSON_Delete(item);
return NULL;
}
}
return item;
}
//实现真正的赋值
static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
{
size_t length = 0;
unsigned char *copy = NULL;
if (string == NULL)return NULL;
length = strlen((const char*)string) + sizeof("");
copy = (unsigned char*)hooks->allocate(length);
if (copy == NULL)return NULL;
memcpy(copy, string, length);
return copy;
}
/* Delete a cJSON structure.
删除cJSON结构
通过递归方法从里到外逐步删除
*/
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
{
cJSON *next = NULL;
while (item != NULL)
{
next = item->next;//获得同层次下相邻兄弟json
//判断如果是json对象,则继续递归遍历内层
if (!(item->type & cJSON_IsReference) && (item->child != NULL))
{
cJSON_Delete(item->child);
}
//如果是其他数据类型,则直接通过地址释放内存
if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
{
global_hooks.deallocate(item->valuestring);
}
if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
{
global_hooks.deallocate(item->string);
}
//先释放键值,再释放键名。这些数据都是通过指针存储管理
global_hooks.deallocate(item);
item = next;
}
}
同理,追加的键值为不同类型时,所使用的不同函数,其具体原理是相似的
cJSON_AddNumberToObject(root, "width", 1920);
cJSON_AddFalseToObject(root, "interlace");