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");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值