首先看下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;
根据注释里面的成员变量作用可大致了解:
了解主要的存储结构后,看一下如何初始化一个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;//返回地址}
说明函数调用顺序:
对于新创建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");