因为最近在和ONENET云平台数据通信相关的项目中遇到对JSON数据的处理,于是对cJSON进行了深入研究,感觉收益匪浅,如沐春风,当然个人觉得代码格式阅读起来不是特别方便。前人种树,后人乘凉,希望整理的内容可以给道友门学习cJSON的时候提供绵薄之力,当然不足之处请各位道友批评指正,下面是我在学习过程中的相关资料。
一、JSON
先对JSON的数据格式做一个简要的介绍。
1、定义
百度百科对JSON的定义是:JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。
2、JSON语法规则
- 数据在名称/值对中
- 数据由括号分隔
- 大括号保存对象
- 中括号保存数组
3、JSON值
JSON 值可以是:
- 数字(整数或浮点数)
{ "age":30 }
- 字符串(在双引号中)
{"name" : “brook”}
- 逻辑值(true 或 false)
{ “flag” : true }
- 对象(在大括号中)
对象可以包含多个 key/value(键/值)对。
{ "name”:"brook" , “age”:24 }
- 数组(在中括号中)
数组可包含多个对象:
{
"sites": [
{ "name":"菜鸟教程" , "url":"www.runoob.com" },
{ "name":"google" , "url":"www.google.com" },
{ "name":"微博" , "url":"www.weibo.com" }
]
}
在上面的例子中,对象 "sites" 是包含三个对象的数组。每个对象代表一条关于某个网站(name、url)的记录。
- null
{ "runoob":null }
二、cJSON结构体
言归正传,回到cJSON,cJSON是C语言中的一个JSON编解码器,在一些面向对象的编程语言(例如C++,Java,Python)中,存在字符串数组、字典等数据结构,可以非常方便地对JSON数据进行处理,而C语言中只能使用结构体来处理JSON数据,学习使用cJSON把cJSON结构体作为切入点是正确的选择。cJSON结构体如下所示:
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
从上面的cJSON结构体可以看出,cJSON数据结构为多级双向链表(初步猜测,才疏学浅,不喜勿喷)。结构体由数据指针、数据项类型、数据和节点名称4部分组成。
- 数据指针:包括指向双向链表前驱和后继的指针prev和next,指向孩子节点的child指针。
- 数据项类型:有七种数据项类型,False,True,NULL,数字,字符串,数组,对象。
- 数据:这里需要注意的是,当数据项类型为数字时,数据将在valueint和valuedouble中同时保存。
- 节点名称可能为空
光说不练假把式,先拿第一部分的数组举个栗子,个人觉得最终在内存中形成的结构应该如下所示(没有好的画图工具,用了visio,xx画质,请各位见谅):
个人做了以下总结:
- 遇到大括号或中括号,如果没有形成键值对关系的,一般节点名称为空。
- 逗号分隔的为属于同一层的节点,存在前驱和后继的关系。
- 分级的层次关系通过child指针表示。
三、内存管理
1、内存管理结构体
cJSON使用Hooks进行内存管理,Hooks结构体有内存分配和内存释放函数组成,如下所示:
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
通过Hooks初始化函数可以看出,如果hooks没有自定义的内存分配和释放函数,默认使用malloc和free函数。
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;
}
2、创建节点
创建节点过程很简单,先进行内存分配,然后将内存清0,即数据存储区域为空,指针域指向NULL。
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
通过调用cJSON_New_Item创建基本类型的节点,都是先调用cJSON_New_Item,然后将类型变量设置为相应类型,还有一部分给数据单元赋值(比如bool、number、string类型):
/* 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_CreateArray创建4种基本类型的数组,用于批量创建节点:
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray(