飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 2803|回复: 1

[C/C++] cjson源码阅读

[复制链接]

该用户从未签到

发表于 2016-5-6 21:28:05 | 显示全部楼层 |阅读模式
本帖最后由 stucky 于 2016-5-6 22:36 编辑

基本信息
在阅读源码之前可以先看看json的介绍 中文介绍

在看完官网的介绍后,我们知道 json 的 value 存在这么几种类型: 对象, 数组, 字符串, 数字, true, false, null。

其中对象是一个 key-value 的集合, 而数组是一些 value 的有序列表。


来了解下cjson的结构体定义 和类型的定义
[C] 纯文本查看 复制代码
//定义cjson值的类型
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5
#define cJSON_Object 6
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512

//json为一项"键-值"的数据
typedef struct cJSON {
        //同一级的cjson结构 利用双向链表存储
        struct cJSON *next, *prev;        

        //如果是object 或 array的话 则为第一个儿子的指针
//也就是object 或 array中 可以嵌套object 或 array
        struct cJSON *child;

        //本项的类型
        int type;
        //value
        char *valuestring;
        int valueint;
        double valuedouble;
        //key
        char *string;
} cJSON;


内存管理
[C] 纯文本查看 复制代码
typedef struct cJSON_Hooks {
        void *(*malloc_fn)(size_t sz);
        void(*free_fn)(void *ptr);
} cJSON_Hooks;

void cJSON_InitHooks(cJSON_Hooks* hooks)
{
        if (!hooks) { /* Reset hooks */
                cJSON_malloc = malloc;
                cJSON_free = free;
                return;
        }
        cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc;
        cJSON_free = (hooks->free_fn) ? hooks->free_fn : free;
}

cjson使用的内存管理为c语言中常用的malloc和free
这里使用了hook技术使得用户可以自定义内存管理

创建节点
[C] 纯文本查看 复制代码
//返回一个cjson结构指针
static cJSON *cJSON_New_Item(void)
{
        cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
        if (node) memset(node, 0, sizeof(cJSON));
        return node;
}


根据对应的类型来生成对应类型的节点
[C] 纯文本查看 复制代码
//创建基础类型
cJSON *cJSON_CreateNull(void)
{
        cJSON *item = cJSON_New_Item();
        if (item)item->type = cJSON_NULL;
        return item;
}

cJSON *cJSON_CreateTrue(void)
{
        cJSON *item = cJSON_New_Item();
        if (item)item->type = cJSON_True;
        return item;
}

cJSON *cJSON_CreateFalse(void)
{
        cJSON *item = cJSON_New_Item();
        if (item)item->type = cJSON_False;
        return item;
}

cJSON *cJSON_CreateBool(int b)
{
        cJSON *item = cJSON_New_Item();
        if (item)item->type = b ? cJSON_True : cJSON_False;
        return item;
}

cJSON *cJSON_CreateNumber(double num)
{
        cJSON *item = cJSON_New_Item();
        if (item)
        {
                item->type = cJSON_Number;
                item->valuedouble = num;
                item->valueint = (int)num;
        }
        return item;
}

cJSON *cJSON_CreateString(const char *string)
{
        cJSON *item = cJSON_New_Item();
        if (item)
        {
                item->type = cJSON_String;
                item->valuestring = cJSON_strdup(string);
        }
        return item;
}
cJSON *cJSON_CreateArray(void)
{
        cJSON *item = cJSON_New_Item();
        if (item)item->type = cJSON_Array;
        return item;
}

cJSON *cJSON_CreateObject(void)
{
        cJSON *item = cJSON_New_Item();
        if (item)item->type = cJSON_Object;
        return item;
}

//创建数组
cJSON *cJSON_CreateIntArray(const int *numbers, int count)                
{ 
        int i; 
        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); 
        for (i = 0; a && i < count; i++)
        { 
                n = cJSON_CreateNumber(numbers[i]); 
                if (!i)
                        a->child = n; 
                else suffix_object(p, n); 
                p = n; 
        }
        return a; 
}

cJSON *cJSON_CreateFloatArray(const float *numbers, int count)        
{ 
        int i; 
        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); 
        for (i = 0; a && i < count; i++)
        { 
                n = cJSON_CreateNumber(numbers[i]); 
                if (!i)
                        a->child = n; 
                else 
                        suffix_object(p, n); 
                p = n; 
        }
        return a; 
}

cJSON *cJSON_CreateDoubleArray(const double *numbers, int count)        
{ 
        int i; 
        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); 
        for (i = 0; a && i < count; i++)
        { 
                n = cJSON_CreateNumber(numbers[i]); 
                if (!i)
                        a->child = n; 
                else 
                        suffix_object(p, n); 
                p = n; 
        }
        return a; 
}

cJSON *cJSON_CreateStringArray(const char **strings, int count)        
{ 
        int i; 
        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray(); 
        for (i = 0; a && i < count; i++)
        { 
                n = cJSON_CreateString(strings[i]); 
                if (!i)
                        a->child = n;
                else suffix_object(p, n); 
                p = n; 
        }
        return a; 
}


在处理字符串的时候会用到一个cJSON_strdup函数 实则为申请空间,将字符串复制到此处空间中
[C] 纯文本查看 复制代码
//申请空间并复制字符串
static char* cJSON_strdup(const char* str)
{
        size_t len;
        char* copy;

        len = strlen(str) + 1;
        if (!(copy = (char*)cJSON_malloc(len))) return 0;
        memcpy(copy, str, len);
        return copy;
}


删除节点

删除节点很简单 删除儿子
如果是字符串类型 删除字符串的value
最后删除key
最后将自己删除

[C] 纯文本查看 复制代码
//释放json结构体
void cJSON_Delete(cJSON *c)
{
        cJSON *next;
        while (c)
        {
                next = c->next;
                if (!(c->type&cJSON_IsReference) && c->child) 
                        cJSON_Delete(c->child);
                if (!(c->type&cJSON_IsReference) && c->valuestring) 
                        cJSON_free(c->valuestring);
                if (!(c->type&cJSON_StringIsConst) && c->string) 
                        cJSON_free(c->string);
                cJSON_free(c);
                c = next;
        }
}


节点管理

json可以理解为一棵树
节点操作就是把节点b 添加为节点a的儿子 或者从节点a中把节点b删除
修改节点a 查询节点a
这就是数据结构中的增删改查

添加儿子节点
添加儿子节点有两种操作 一种是给object添加儿子 一种是给array添加儿子

[C] 纯文本查看 复制代码
//对双向链表进行处理
static void suffix_object(cJSON *prev, cJSON *item) 
{ 
        prev->next = item; 
        item->prev = prev; 
}

//增加项到数组或对象中
void cJSON_AddItemToArray(cJSON *array, cJSON *item)
{
        cJSON *c = array->child;
        if (!item)
                return;
        //如果为空则为头指针
        if (!c)
        {
                array->child = item;
        }
        else
        {
                while (c && c->next)
                        c = c->next;
                suffix_object(c, item);
        }
}

void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{
        if (!item)
                return;
        if (item->string)
                cJSON_free(item->string);
        item->string = cJSON_strdup(string);
        cJSON_AddItemToArray(object, item);
}

void cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
{
        if (!item)
                return;
        if (!(item->type&cJSON_StringIsConst) && item->string)
                cJSON_free(item->string);
        item->string = (char*)string;
        item->type |= cJSON_StringIsConst;
        cJSON_AddItemToArray(object, item);
}


cjson里面定义了一些宏函数来方便我们快速添加各种类型到object里面

[C] 纯文本查看 复制代码
//各种类型添加到object
#define cJSON_AddNullToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateNull())
#define cJSON_AddTrueToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
#define cJSON_AddFalseToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
#define cJSON_AddBoolToObject(object,name,b)        cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
#define cJSON_AddNumberToObject(object,name,n)        cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s)        cJSON_AddItemToObject(object, name, cJSON_CreateString(s))

当一个节点在一个树上的时候,将这个节点插入到另外一个树上的时候,这个节点的prev和next将会被覆盖

cjson提供了一种引用添加节点的方法

[C] 纯文本查看 复制代码
void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
{
        cJSON_AddItemToArray(array, create_reference(item));
}

void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
{
        cJSON_AddItemToObject(object, string, create_reference(item));
}


也就是新创建一个item,新创建的item的value指针和原来的value值一样 这样两个item指向同一个item

删除儿子节点


[C] 纯文本查看 复制代码
cJSON *cJSON_DetachItemFromArray(cJSON *array, int which)                        
{
        cJSON *c = array->child; 
        while (c && which > 0) 
                c = c->next, which--; 
        if (!c) 
                return 0;
        if (c->prev) 
                c->prev->next = c->next; 
        if (c->next) 
                c->next->prev = c->prev; 
        if (c == array->child) 
                array->child = c->next; 
        c->prev = c->next = 0; 
        return c;
}

void cJSON_DeleteItemFromArray(cJSON *array, int which)                        
{ 
        cJSON_Delete(cJSON_DetachItemFromArray(array, which)); 
}

cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) 
{ 
        int i = 0; 
        cJSON *c = object->child; 
        while (c && cJSON_strcasecmp(c->string, string)) 
                i++, c = c->next; 
        if (c) 
                return cJSON_DetachItemFromArray(object, i); 
        return 0; 
}

void cJSON_DeleteItemFromObject(cJSON *object, const char *string) 
{ 
        cJSON_Delete(cJSON_DetachItemFromObject(object, string)); 
}


删除很简单 从object和array中删除即可

但是cjson 提供了一个Detach 也就是把要删除的节点 从树中分离出来 但是不进行内存释放

这样我们可以做很多事,比如添加到另外一个json中

查找节点
[C] 纯文本查看 复制代码
//获取数组中的某项
cJSON *cJSON_GetArrayItem(cJSON *array, int item)                                
{ 
        cJSON *c = array->child;  
        while (c && item > 0) 
                item--, c = c->next; 
        return c; 
}

//根据键值获取项
cJSON *cJSON_GetObjectItem(cJSON *object, const char *string)        
{ 
        cJSON *c = object->child; 
        while (c && cJSON_strcasecmp(c->string, string)) 
                c = c->next; 
        return c; 
}


因为是链表,所以搜索为暴力搜索

修改节点

[C] 纯文本查看 复制代码
void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)                {
        cJSON *c = array->child; 
        while (c && which > 0) 
                c = c->next, which--; 
        if (!c) 
                return;
        newitem->next = c->next; 
        newitem->prev = c->prev; 
        if (newitem->next) 
                newitem->next->prev = newitem;
        if (c == array->child) 
                array->child = newitem; 
        else 
                newitem->prev->next = newitem; 
        c->next = c->prev = 0; 
        cJSON_Delete(c);
}

void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
{ 
        int i = 0; 
        cJSON *c = object->child; 
        while (c && cJSON_strcasecmp(c->string, string))
                i++, c = c->next; 
        if (c)
        { 
                newitem->string = cJSON_strdup(string); 
                cJSON_ReplaceItemInArray(object, i, newitem); 
        } 
}


修改操作很简单,对链表的简单操作

插入操作

[C] 纯文本查看 复制代码
void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)                {
        cJSON *c = array->child; 
        while (c && which > 0) 
                c = c->next, which--; if (!c) 
        { 
                cJSON_AddItemToArray(array, newitem); 
                return; 
        }
        newitem->next = c; 
        newitem->prev = c->prev; 
        c->prev = newitem; 
        if (c == array->child) 
                array->child = newitem; 
        else 
                newitem->prev->next = newitem;
}


插入操作也是简单的链表操作

json解析
整体解析
[C] 纯文本查看 复制代码
//跳过小于等于32的ansi码 也就是跳过空白
static const char *skip(const char *in)
{
        while (in && *in && (unsigned char)*in <= 32)
                in++;
        return in;
}

//创建一个对象 创建一个新的头 并填充
cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, int require_null_terminated)
{
        const char *end = 0;
        cJSON *c = cJSON_New_Item();
        ep = 0;
        if (!c) 
                return 0;
        //分析值
        end = parse_value(c, skip(value));
        //如果分析失败 释放内存 设置ep
        if (!end)        
        { 
                cJSON_Delete(c); 
                return 0; 
        }
        //处理附带垃圾的字符串 
        if (require_null_terminated) 
        { 
                end = skip(end); 
                if (*end) 
                { 
                        cJSON_Delete(c); 
                        ep = end; 
                        return 0; 
                } 
        }
        //是否返回处理后的字符串
        if (return_parse_end) 
                *return_parse_end = end;
        return c;
}


上面两个函数 我们需要了解一下parse_value 和 skip

skip很简单 其实就是跳过字符串中的空白

parse_value就是根据前几个字符来判断是什么类型

[C] 纯文本查看 复制代码
static const char *parse_value(cJSON *item, const char *value)
{
        if (!value)                                                return 0;        /* Fail on null. */
        if (!strncmp(value, "null", 4))        { item->type = cJSON_NULL;  return value + 4; }
        if (!strncmp(value, "false", 5))        { item->type = cJSON_False; return value + 5; }
        if (!strncmp(value, "true", 4))        { item->type = cJSON_True; item->valueint = 1;        return value + 4; }
        if (*value == '\"')                                { return parse_string(item, value); }
        if (*value == '-' || (*value >= '0' && *value <= '9'))        { return parse_number(item, value); }
        if (*value == '[')                                { return parse_array(item, value); }
        if (*value == '{')                                { return parse_object(item, value); }
        ep = value; return 0;        /* failure. */
}


解析字符串部分

解析字符串时 需要对转义字符和utf8字符进行处理

[C] 纯文本查看 复制代码
static const char *parse_string(cJSON *item, const char *str)
{
        const char *ptr = str + 1; char *ptr2; char *out; int len = 0; unsigned uc, uc2;
        if (*str != '\"') { ep = str; return 0; }        /* not a string! */

        //跳过引号和转移符号并得到字符串的长度
        while (*ptr != '\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++;        /* Skip escaped quotes. */

        //申请一块大小等于字符串长度的内存
        out = (char*)cJSON_malloc(len + 1);
        if (!out) return 0;

        ptr = str + 1; ptr2 = out;
        while (*ptr != '\"' && *ptr)
        {
                //如果不是转义字符 则得到字符串
                if (*ptr != '\\')
                        *ptr2++ = *ptr++;
                else
                {
                        //如果是转义字符 进行相应的转换
                        ptr++;
                        switch (*ptr)
                        {
                        case 'b': *ptr2++ = '\b';        break;
                        case 'f': *ptr2++ = '\f';        break;
                        case 'n': *ptr2++ = '\n';        break;
                        case 'r': *ptr2++ = '\r';        break;
                        case 't': *ptr2++ = '\t';        break;
                        case 'u':         /* transcode utf16 to utf8. */
                                uc = parse_hex4(ptr + 1); ptr += 4;        /* get the unicode char. */

                                if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)        break;        /* check for invalid.        */

                                if (uc >= 0xD800 && uc <= 0xDBFF)        /* UTF16 surrogate pairs.        */
                                {
                                        if (ptr[1] != '\\' || ptr[2] != 'u')        break;        /* missing second-half of surrogate.        */
                                        uc2 = parse_hex4(ptr + 3); ptr += 6;
                                        if (uc2 < 0xDC00 || uc2>0xDFFF)                break;        /* invalid second-half of surrogate.        */
                                        uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
                                }

                                len = 4; if (uc < 0x80) len = 1; else if (uc < 0x800) len = 2; else if (uc < 0x10000) len = 3; ptr2 += len;

                                switch (len) {
                                case 4: *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6;
                                case 3: *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6;
                                case 2: *--ptr2 = ((uc | 0x80) & 0xBF); uc >>= 6;
                                case 1: *--ptr2 = (uc | firstByteMark[len]);
                                }
                                ptr2 += len;
                                break;
                        default:  *ptr2++ = *ptr; break;
                        }
                        ptr++;
                }
        }
        *ptr2 = 0;
        if (*ptr == '\"') ptr++;
        //设置项的类型和值
        item->valuestring = out;
        item->type = cJSON_String;
        //处理完后返回
        return ptr;
}

static unsigned parse_hex4(const char *str)
{
        unsigned h = 0;
        if (*str >= '0' && *str <= '9') h += (*str) - '0'; else if (*str >= 'A' && *str <= 'F') h += 10 + (*str) - 'A'; else if (*str >= 'a' && *str <= 'f') h += 10 + (*str) - 'a'; else return 0;
        h = h << 4; str++;
        if (*str >= '0' && *str <= '9') h += (*str) - '0'; else if (*str >= 'A' && *str <= 'F') h += 10 + (*str) - 'A'; else if (*str >= 'a' && *str <= 'f') h += 10 + (*str) - 'a'; else return 0;
        h = h << 4; str++;
        if (*str >= '0' && *str <= '9') h += (*str) - '0'; else if (*str >= 'A' && *str <= 'F') h += 10 + (*str) - 'A'; else if (*str >= 'a' && *str <= 'f') h += 10 + (*str) - 'a'; else return 0;
        h = h << 4; str++;
        if (*str >= '0' && *str <= '9') h += (*str) - '0'; else if (*str >= 'A' && *str <= 'F') h += 10 + (*str) - 'A'; else if (*str >= 'a' && *str <= 'f') h += 10 + (*str) - 'a'; else return 0;
        return h;
}


解析数字部分

number.gif

[C] 纯文本查看 复制代码
static const char *parse_number(cJSON *item, const char *num)
{
        double n = 0, sign = 1, scale = 0; int subscale = 0, signsubscale = 1;

        if (*num == '-') sign = -1, num++;        /* Has sign? */
        if (*num == '0') num++;                        /* is zero */
        if (*num >= '1' && *num <= '9')        do        n = (n*10.0) + (*num++ - '0');        while (*num >= '0' && *num <= '9');        /* Number? */
        if (*num == '.' && num[1] >= '0' && num[1] <= '9') { num++;                do        n = (n*10.0) + (*num++ - '0'), scale--; while (*num >= '0' && *num <= '9'); }        /* Fractional part? */
        if (*num == 'e' || *num == 'E')                /* Exponent? */
        {
                num++; if (*num == '+') num++;        else if (*num == '-') signsubscale = -1, num++;                /* With sign? */
                while (*num >= '0' && *num <= '9') subscale = (subscale * 10) + (*num++ - '0');        /* Number? */
        }

        n = sign*n*pow(10.0, (scale + subscale*signsubscale));        /* number = +/- number.fraction * 10^+/- exponent */

        item->valuedouble = n;
        item->valueint = (int)n;
        item->type = cJSON_Number;
        return num;
}


解析数组

解析数组的时候先需要遇到'[' 然后挨个读取节点的内容 节点用,进行分割,前后还可以能有空白需要跳过 最后以']'结尾

[C] 纯文本查看 复制代码
static const char *parse_array(cJSON *item, const char *value)
{
        cJSON *child;
        if (*value != '[')        { ep = value; return 0; }        /* not an array! */

        item->type = cJSON_Array;
        value = skip(value + 1);
        if (*value == ']') return value + 1;        /* empty array. */

        item->child = child = cJSON_New_Item();
        if (!item->child) return 0;                 /* memory fail */
        value = skip(parse_value(child, skip(value)));        /* skip any spacing, get the value. */
        if (!value) return 0;

        while (*value == ',')
        {
                cJSON *new_item;
                if (!(new_item = cJSON_New_Item())) return 0;         /* memory fail */
                child->next = new_item; new_item->prev = child; child = new_item;
                value = skip(parse_value(child, skip(value + 1)));
                if (!value) return 0;        /* memory fail */
        }

        if (*value == ']') return value + 1;        /* end of array */
        ep = value; return 0;        /* malformed. */
}


解析对象

解析对象和解析数组类似 对象的儿子里面有key-value key是字符串 value可以是任何值 key和value用":"分隔

[C] 纯文本查看 复制代码
static const char *parse_object(cJSON *item, const char *value)
{
        cJSON *child;
        if (*value != '{')        { ep = value; return 0; }        /* not an object! */

        item->type = cJSON_Object;
        value = skip(value + 1);
        if (*value == '}') return value + 1;        /* empty array. */

        //创建新的项
        item->child = child = cJSON_New_Item();
        if (!item->child) return 0;

        //为新的项建立对应的键名
        value = skip(parse_string(child, skip(value)));
        if (!value) return 0;
        child->string = child->valuestring;
        child->valuestring = 0;
        if (*value != ':') { ep = value; return 0; }        /* fail! */
        //为新的项建立对应的值
        value = skip(parse_value(child, skip(value + 1)));        /* skip any spacing, get the value. */
        if (!value) return 0;

        while (*value == ',')
        {
                cJSON *new_item;
                if (!(new_item = cJSON_New_Item()))        return 0; /* memory fail */

                //连接object
                child->next = new_item;
                new_item->prev = child;
                child = new_item;

                value = skip(parse_string(child, skip(value + 1)));
                if (!value) return 0;
                child->string = child->valuestring; child->valuestring = 0;
                if (*value != ':') { ep = value; return 0; }        /* fail! */
                value = skip(parse_value(child, skip(value + 1)));        /* skip any spacing, get the value. */
                if (!value) return 0;
        }

        if (*value == '}') return value + 1;        /* end of array */
        ep = value; return 0;        /* malformed. */
}


json的输出

json的输出 有格式化输出和非格式化输出
这里就不贴源码出来了,有需要的自己可以看看

最重要的是要不要输出一些空白的问题。

到此 整个json阅读完毕




PYG19周年生日快乐!
  • TA的每日心情
    无聊
    2018-11-24 13:33
  • 签到天数: 8 天

    [LV.3]偶尔看看II

    发表于 2016-5-12 07:01:15 | 显示全部楼层
    这是啥呀 ,没搞懂
    PYG19周年生日快乐!
    回复 支持 反对

    使用道具 举报

    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表