cjson源码阅读
本帖最后由 stucky 于 2016-5-6 22:36 编辑基本信息
在阅读源码之前可以先看看json的介绍 中文介绍
在看完官网的介绍后,我们知道 json 的 value 存在这么几种类型: 对象, 数组, 字符串, 数字, true, false, null。
其中对象是一个 key-value 的集合, 而数组是一些 value 的有序列表。
来了解下cjson的结构体定义 和类型的定义
//定义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;
内存管理
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技术使得用户可以自定义内存管理
创建节点
//返回一个cjson结构指针
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node, 0, sizeof(cJSON));
return node;
}
根据对应的类型来生成对应类型的节点
//创建基础类型
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);
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);
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);
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);
if (!i)
a->child = n;
else suffix_object(p, n);
p = n;
}
return a;
}
在处理字符串的时候会用到一个cJSON_strdup函数 实则为申请空间,将字符串复制到此处空间中
//申请空间并复制字符串
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
最后将自己删除
//释放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添加儿子
//对双向链表进行处理
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里面
//各种类型添加到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提供了一种引用添加节点的方法
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
删除儿子节点
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中
查找节点
//获取数组中的某项
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;
}
因为是链表,所以搜索为暴力搜索
修改节点
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);
}
}
修改操作很简单,对链表的简单操作
插入操作
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解析
整体解析
//跳过小于等于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就是根据前几个字符来判断是什么类型
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字符进行处理
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 != '\\' || ptr != '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);
}
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;
}
解析数字部分
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 >= '0' && num <= '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;
}
解析数组
解析数组的时候先需要遇到'[' 然后挨个读取节点的内容 节点用,进行分割,前后还可以能有空白需要跳过 最后以']'结尾
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用":"分隔
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阅读完毕
这是啥呀 ,没搞懂
页:
[1]