cJSON的学习
Posted Shemesz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cJSON的学习相关的知识,希望对你有一定的参考价值。
一、JSON介绍
JSON 全称 javascript Object Notation,即 JS对象简谱,是一种轻量级的数据格式。
JSON的语法规则
JSON对象是一个无序的“名字/值”的键值对集合:
- 以"{“开始,以”}"结束,允许嵌套使用;
- 每个名称和值成对出现,名称和值之间用“ :”分开
- 键值对之间用“ :”分隔
- 在这些字符前后允许存在无意义的空白符
对于键值,可以有如下值:
- 一个新的json对象
- 数组:使用"[“和”]"表示
- 字符串:使用引号"表示
- 字面值:false、null、true中的一个(必须是小写)
示例如下
{
"name": "mk12345",
"age": 21,
"weight": 60
"address":
{
"country": "China",
"zip-code": 111111
},
"skill": ["c", "Java", "Python"],
"student": false
}
二、cJSON介绍
cJSON是一个超轻巧,携带方便,单文件,简单的可以作为ANSI-C标准的JSON解析器。
问题:我们为什么选择cJSON来解析JSON字符串呢?
- 答案:因为简洁又简单,而且效率又快,cJSON工程文件也非常简单,仅一个.C文件和一个.h文件!并且文件体积大小不到K。
首先,我们可以通过此链接下载cJOSN:https://sourceforge.net/projects/cjson/
了解cJSON,首先就要了解一下cJSON的结构体
- cJSON结构体
typedef struct cJSON {
struct cJSON*next,*prev; /* 遍历数组或对象链的前向或后向链表指针*/
struct cJSON *child; /*数组或对象的孩子节点*/
int type; /* key的类型*/
char *valuestring; /*字符串值*/
int valueint; /* 整数值*/
double valuedouble; /* 浮点数值*/
char *string; /* key的名字*/
} cJSON;
说明:
1. cJSON是使用链表来存储数据的,其访问方式很像一棵树。每一个节点可以有兄弟节点,通过next/prev指针来查找,它类似双向链表;每个节点也可以有孩子节点,通过child指针来访问,进入下一层。只有节点是对象或数组时才可以有孩子节点。
2. type是键(key)的类型,一共有种取值,分别是:False,True,null,Number,String,Array,Object。若是Number类型,则valueint或valuedouble种存储着值,如期望的是int,则访问valueint,如期望的是double,则访问valuedouble,可以得到值。若是String类型的,则valuestring中存储着值,可以访问valuestring得到值。
3. string中存放的是这个节点的名字,可以理解位key的名称。
三、封装cJSON
封装方法:
封装JSON数据的过程,其实就是创建链表和向链表中添加节点的过程。
先了解以下名词:
- 头指针:指向链表头结点的指针;
- 头结点:不存放有效数据,方便链表操作;
- 首节点:第一个存放有效数据的节点;
- 尾节点:最后一个存放有效数据的节点;
明白了这几个概念之后,我们开始讲述创建一段完整的JSON数据,即如何创建一条完整的链表。
1. 创建头指针
cJOSN* cjosn_test = NULL;
2. 创建头结点,并将头指针指向头结点
cjson_test = cJSON_CreateObject();
3. 向链表中添加节点
cJSON_AddNullToObject(cJSON * const object, const char * const name);
cJSON_AddTrueToObject(cJSON * const object, const char * const name);
cJSON_AddFalseToObject(cJSON * const object, const char * const name);
cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
cJSON_AddObjectToObject(cJSON * const object, const char * const name);
cJSON_AddArrayToObject(cJSON * const object, const char * const name);
4. 输出JSON数据
上面讲述,一段完整的JSON数据就是一条长长的链表,cJSON提供了一个API,可以将整条链表中存放的JSON信息输出到一个字符串中:
(char *) cJSON_Print(const cJSON *item);
5. 示例
#include <stdio.h>
#include "cJSON.h"
int main(void)
{
cJSON* cjson_test = NULL;
cJSON* cjson_address = NULL;
cJSON* cjson_skill = NULL;
char* str = NULL;
/* 创建一个JSON数据对象(链表头结点) */
cjson_test = cJSON_CreateObject();
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddStringToObject(cjson_test, "name", "mculover666");
/* 添加一条整数类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(cjson_test, "age", 22);
/* 添加一条浮点类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(cjson_test, "weight", 55.5);
/* 添加一个嵌套的JSON数据(添加一个链表节点) */
cjson_address = cJSON_CreateObject();
cJSON_AddStringToObject(cjson_address, "country", "China");
cJSON_AddNumberToObject(cjson_address, "zip-code", 111111);
cJSON_AddItemToObject(cjson_test, "address", cjson_address);
/* 添加一个数组类型的JSON数据(添加一个链表节点) */
cjson_skill = cJSON_CreateArray();
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java" ));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));
cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);
/* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */
cJSON_AddFalseToObject(cjson_test, "student");
/* 打印JSON对象(整条链表)的所有数据 */
str = cJSON_Print(cjson_test);
printf("%s\\n", str);
return 0;
}
该JSON数据链表的关系如图:
四、解析cJSON
解析方法
解析JSON数据的过程,其实就是剥离一个一个链表节点(键值对)的过程。
解析方法如下:
1. 创建链表头指针
cJSON* cjson_test = NULL;
2. 解析整段JSON数据,并将链表头结点地址返回,赋值给头指针:
(cJSON *) cJSON_Parse(const char *value);
3. 根据键值对的名称从链表中取出对应的值,返回该键值对(链表节点)的地址
(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
4. 如果JSON数据的值是数组,使用下面的两个API提取数据:
(int) cJSON_GetArraySize(const cJSON *array);
(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
5.解析示例
#include <stdio.h>
#include "cJSON.h"
char *message =
"{ \\
\\"name\\":\\"mculover666\\", \\
\\"age\\": 22, \\
\\"weight\\": 55.5, \\
\\"address\\": \\
{ \\
\\"country\\": \\"China\\",\\
\\"zip-code\\": 111111\\
}, \\
\\"skill\\": [\\"c\\", \\"Java\\", \\"Python\\"],\\
\\"student\\": false \\
}";
int main(void)
{
cJSON* cjson_test = NULL;
cJSON* cjson_name = NULL;
cJSON* cjson_age = NULL;
cJSON* cjson_weight = NULL;
cJSON* cjson_address = NULL;
cJSON* cjson_address_country = NULL;
cJSON* cjson_address_zipcode = NULL;
cJSON* cjson_skill = NULL;
cJSON* cjson_student = NULL;
int skill_array_size = 0, i = 0;
cJSON* cjson_skill_item = NULL;
/* 解析整段JSO数据 */
cjson_test = cJSON_Parse(message);
if(cjson_test == NULL)
{
printf("parse fail.\\n");
return -1;
}
/* 依次根据名称提取JSON数据(键值对) */
cjson_name = cJSON_GetObjectItem(cjson_test, "name");
cjson_age = cJSON_GetObjectItem(cjson_test, "age");
cjson_weight = cJSON_GetObjectItem(cjson_test, "weight");
printf("name: %s\\n", cjson_name->valuestring);
printf("age:%d\\n", cjson_age->valueint);
printf("weight:%.1f\\n", cjson_weight->valuedouble);
/* 解析嵌套json数据 */
cjson_address = cJSON_GetObjectItem(cjson_test, "address");
cjson_address_country = cJSON_GetObjectItem(cjson_address, "country");
cjson_address_zipcode = cJSON_GetObjectItem(cjson_address, "zip-code");
printf("address-country:%s\\naddress-zipcode:%d\\n", cjson_address_country->valuestring, cjson_address_zipcode->valueint);
/* 解析数组 */
cjson_skill = cJSON_GetObjectItem(cjson_test, "skill");
skill_array_size = cJSON_GetArraySize(cjson_skill);
printf("skill:[");
for(i = 0; i < skill_array_size; i++)
{
cjson_skill_item = cJSON_GetArrayItem(cjson_skill, i);
printf("%s,", cjson_skill_item->valuestring);
}
printf("\\b]\\n");
/* 解析布尔型数据 */
cjson_student = cJSON_GetObjectItem(cjson_test, "student");
if(cjson_student->valueint == 0)
{
printf("student: false\\n");
}
else
{
printf("student:error\\n");
}
return 0;
}
6. 注意事项
在本示例中,因为我提前知道数据的类型,比如字符型或者浮点型,所以我直接使用指针指向对应的数据域提取,在实际使用时,如果提前不确定数据类型,应该先判断type的值,确定数据类型,再从对应的数据域中提取数据。
7. 内存及时释放
cJSON的所有操作都是基于链表的,所以cJSON在使用过程中大量的使用malloc从堆中分配动态内存的,所以在使用完之后,应当及时调用下面的函数,清空cJSON指针所指向的内存,该函数也可用于删除某一条数据:
(void) cJSON_Delete(cJSON *item);
注意:该函数删除一条JSON数据时,如果有嵌套,会连带删除。
以上是关于cJSON的学习的主要内容,如果未能解决你的问题,请参考以下文章