cJSON源码分析(一)

2021/5/7 22:26:59

本文主要是介绍cJSON源码分析(一),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

首先看下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");

               



这篇关于cJSON源码分析(一)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程