cJosn

看 cJson 源码之前,先了解下 Json 的格式。

json 格式

官方中文翻译:http://www.json.org/json-zh.html

Json 数据的核心就是 “key - value”,也就是 “名 - 值”,这个对应关系。

下面摘自官方说明

1
2
3
4
5
JSON建构于两种结构:

“名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象(object),纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。
这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。

JSON 的数据类型如下

  1. object 对象
  2. array,值(value)的有序集合
  3. value,可以是双引号括起来的字符串(string)、数值(number)、true、false、 null、对象(object)或者数组(array)。可以嵌套
  4. string,由双引号包围的任意数量Unicode字符的集合,使用反斜线转义
  5. number 数值
  6. 空白,也就是空格。

下面,假设我们来写这个cJson,如何设计。从这个角度,来分析cJson的源码。

数据类型

前面提到了 json 的数据类型,那么对其进行定义,源码头文件 cJosn.h 中。

1
2
3
4
5
6
7
8
9
10
11
/* cJSON Types: */
#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

源码中用 define 。如果大家看过 effective C++ 这本书,书中的建议是尽量用 const enum inline 代替。
那这里可以用

1
2
3
4
const int cJSON_False = 0;
...
或者
enum{cJSON_False, cJSON_True, cJSON_NULL, cJSON_Number, cJSON_String, cJSON_Array, cJSON_Object, cJSON_IsReference=256, cJSON_StringIsConst=512}

数据结构

接着源码中定义了一个名为 cJson 的结构体,来描述 json 数据的每个节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* prev/next 就是前后链表指针 */
struct cJSON *child; /* 子节点指针 */

int type; /* key 的类型,也就上面 define 定义的 */

char *valuestring; /* 字符串值 */
int valueint; /* 整数值 */
double valuedouble; /* 浮点数值 */

char *string; /* key 的名字 */
} cJSON;

这里可以明白,cJson用链表来处理存储数据,访问方式类似一棵树。

内存管理

在 c 语言中内存分配和释放一般用 malloc 和 free 。这里 cjson 使用 Hook 来让我们可以自定义 malloc 和 free,来进行管理。我们可以 cJSON_InitHooks 函数替换默认使用的 malloc ,free。源码中的函数声明和定义分开了,我放在下面一起好看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
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;
}

节点操作

创建节点

前面定义了数据类型和结构,有了内存管理,下面创建一个新的节点。

1
2
3
4
5
6
7
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}

static 定义为静态函数,用 cJSON_malloc 分配内存。然后,在这个节点中创建一些具体的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/* Create basic types: */
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;}

删除节点

先要删除子节点,然后释放内存。
对于 Object,array 删除子节点,在删除自身。
对于 string 先要删除字符串部分,然后再删除自己。
上面这样做,是因为子节点和string部分都是动态分配的内存,要我们自己去控制。
其他类型节点,直接删除节点本身就行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Delete a cJSON structure. */
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;
}
}

添加节点

接着,添加节点只有2种情况,给 object 添加,给 array 添加。
给 object 添加的做法只是比 array 多一个 key 值,也就是 string 。那么,可以先给 object 添加 key 之后调用给 array 添加节点的函数来。
下面先是写给 array 添加节点的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
void cJSON_AddItemToArray(cJSON *array, cJSON *item)
{
cJSON *c = array->child;
if (!item) return;
if (!c) { // 如果 array 没有子节点,则添加 item
array->child = item;
}
else { // 如果 array 有子节点,子节点之间互为兄弟节点,
while (c && c->next) // 所以找最后一个子节点,添加 item ,item 与他们也互为兄弟节点
c = c->next;
suffix_object(c, item); // c,item 是兄弟节点
}
}

另外可以有多个子节点,多个子节点之间互为兄弟节点,所以他们有指针互相指向对方,用于遍历该 object/arrary 的所有节点。

1
2
3
4
static void suffix_object(cJSON *prev, cJSON *item){
prev->next = item;
item->next = prev;
}

给 object 添加节点

1
2
3
4
5
6
7
8
9
10
void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
{
if (!item) return;
if (item->string) { // 如果 item 有 key,需要释放掉
cJSON_free(item->string);
}
item->string = cJSON_strdup(string); // 重新设置 key
cJSON_AddItemToArray(object, item); // 添加子节点

}

到此,上面对节点的操作都具备了。但是 cJson 中用 define 封装了基本类型的节点添加操作,主要是为了方便。比如添加一个 key 为 null 的节点,正常情况下调用函数如下。

1
2
cJSON_AddItemToObject(object,name,cJSON_NULL) // 第三个参数是 key 
`

封装后就如下。

1
cJSON_AddNullToObject(object,name)

所以封装的部分如下。

1
2
3
4
5
6
7
/* Macros for creating things quickly. */
#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))

并且,还定义了批量创建节点的函数。

1
2
3
4
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
extern cJSON *cJSON_CreateStringArray(const char **strings,int count);

未完。

。。。。

Reference

https://blog.csdn.net/yzhang6_10/article/details/51615089
http://github.tiankonguse.com/blog/2014/12/18/cjson-source.html
https://blog.csdn.net/lintax/article/details/50993958
https://github.com/faycheng/cJSON