LV010-cJSON简介
本文主要是C语言基础——cJSON简介与使用的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
一、cJSON简介
1. cJSON是什么?
cJSON是一个使用C语言编写的JSON数据解析器,具有超轻便,可移植,单文件的特点,使用MIT开源协议。项目托管在Github上,仓库地址在这里:GitHub - DaveGamble/cJSON: Ultralightweight JSON parser in ANSI C
2. 源码说明
我们可以用以下命令拉取cJSON源码:
git clone https://github.com/DaveGamble/cJSON.git从Github拉取cJSON源码后,文件非常多,但是其中cJSON的源码文件只有两个:cJSON.h和cJSON.c。使用的时候,只需要将这两个文件复制到工程目录,然后包含头文件cJSON.h即可,如下:
#include "cJSON.h"Tips:我拉取的时候,最新一次提交是这个:DaveGamble/cJSON at 12c4bf1986c288950a3d06da757109a6aa1ece38
3. 源码编译
下载完源码后,仓库是有提供demo的,我们可以直接编译的,说明文档在这里:GitHub - DaveGamble/cJSON: Ultralightweight JSON parser in ANSI C:
# 生成Makefile文件
mkdir build
cd build
cmake ..
# 编译
make但是其实可以直接在源码目录下执行make命令进行编译,编译完毕会得到一个名为 cJSON_test 的可执行文件,我们直接./就可以运行。
二、cJSON源码说明
1. cJSON数据结构
cJSON使用cJSON结构体来表示一个JSON数据,定义在 cJSON.h中,源码如下:
/* The cJSON structure: */
typedef struct cJSON
{
/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *next;
struct cJSON *prev;
/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
struct cJSON *child;
/* The type of the item, as above. */
int type;
/* The item's string, if type==cJSON_String and type == cJSON_Raw */
char *valuestring;
/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
int valueint;
/* The item's number, if type==cJSON_Number */
double valuedouble;
/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
char *string;
} cJSON;首先,它不是将一整段JSON数据抽象出来,而是将其中的一条JSON数据抽象出来,也就是一个键值对,用上面的结构体 strcut cJSON 来表示,其中用来存放值的成员列表如下:
int type; // 用于表示该键值对中值的类型
char *valuestring; // 如果键值类型(type)是字符串,则将该指针指向键值
int valueint; // 如果键值类型(type)是整数,则将该指针指向键值
double valuedouble; // 如果键值类型(type)是浮点数,则将该指针指向键值
char *string; // 用于表示该键值对的名称其次,一段完整的JSON数据中由很多键值对组成,并且涉及到键值对的查找、删除、添加,所以使用链表来存储整段JSON数据,如上面的代码所示:
struct cJSON *next; // 指向下一个键值对
struct cJSON *prev; // 指向上一个键值对最后,因为JSON数据支持嵌套,所以一个键值对的值会是一个新的JSON数据对象(一条新的链表),也有可能是一个数组,方便起见,在cJSON中,数组也表示为一个数组对象,用链表存储,所以:
struct cJSON *child; // 在键值对结构体中,当该键值对的值是一个嵌套的JSON数据或者一个数组时,由child指针指向该条新链表。2. JSON数据封装
封装JSON数据的过程,其实就是创建链表和向链表中添加节点的过程。
cJSON *root = NULL;- (2)创建头结点,并将头指针指向头结点:cJSON/test.c at master · DaveGamble/cJSON · GitHub
root = cJSON_CreateObject();/* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);3. 输出JSON数据
一段完整的JSON数据就是一条长长的链表,那么,如何打印出这段JSON数据呢?cJSON提供了一个API,可以将整条链表中存放的JSON信息输出到一个字符串中:cJSON/cJSON.c at master · DaveGamble/cJSON · GitHub
/* Render a cJSON item/entity/structure to text. */
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
{
return (char*)print(item, true, &global_hooks);
}print函数定义在这里:cJSON/cJSON.c at master · DaveGamble/cJSON · GitHub。这里就不详细去了解了,就是一个遍历链表的过程。
使用cJSON_Print()函数打印JSON数据的时候,只需要接收该函数返回的指针地址即可。
4. JSON数据解析
解析JSON数据的过程,其实就是剥离一个一个链表节点(键值对)的过程。
- (1)创建链表头指针
cJSON* root = NULL;- (2)解析整段JSON数据,并将链表头结点地址返回,赋值给头指针
//CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); // 解析整段数据使用的API只有这一个
root = cJSON_Parse(JSON_data);- (3)根据键值对的名称从链表中取出对应的值,返回该键值对(链表节点)的地址
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)- (4)如果JSON数据的值是数组,使用下面的两个API提取数据
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);三、使用实例
1. JSON数据封装实例
1.1 JSON数据
这里设计一个JSON的结构:
{
"name": "sumu",
"age": 28,
"weight": 69.8,
"address":
{
"country": "China",
"city": "shanghai",
"zip-code": 999999
},
"skill": ["c", "html", "Python"],
"student": false
}1.2 代码编写
#include <stdio.h>
#include "cJSON.h"
int main(int argc, const char * argv[])
{
cJSON* root = NULL;
cJSON* cjson_address = NULL;
cJSON* cjson_skill = NULL;
char* str = NULL;
/* 创建一个JSON数据对象(链表头结点) */
root = cJSON_CreateObject();
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddStringToObject(root, "name", "sumu");
/* 添加一条整数类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(root, "age", 28);
/* 添加一条浮点类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(root, "weight", 69.8);
/* 添加一个嵌套的JSON数据(添加一个链表节点) */
cjson_address = cJSON_CreateObject();
cJSON_AddStringToObject(cjson_address, "country", "China");
cJSON_AddStringToObject(cjson_address, "city", "shanghai");
cJSON_AddNumberToObject(cjson_address, "zip-code", 999999);
cJSON_AddItemToObject(root, "address", cjson_address);
/* 添加一个数组类型的JSON数据(添加一个链表节点) */
cjson_skill = cJSON_CreateArray();
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "html" ));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));
cJSON_AddItemToObject(root, "skill", cjson_skill);
/* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */
cJSON_AddFalseToObject(root, "student");
/* 打印JSON对象(整条链表)的所有数据 */
str = cJSON_Print(root);
printf("%s\n", str);
return 0;
}1.3 编译源码
gcc cJSON.c main.c -o cJSON_test1.4 运行测试
我们直接执行即可:
sumu@sumu-virtual-machine:~/7Linux/cJSON-master$ ./cJSON_test
{
"name": "sumu",
"age": 28,
"weight": 69.8,
"address": {
"country": "China",
"city": "shanghai",
"zip-code": 999999
},
"skill": ["C", "html", "Python"],
"student": false
}1.5 数据链表关系图
我根据自己的理解画了个简图如下,来帮助理解:

2. JSON数据解析实例
2.1 JSON数据
char *JSON_data =
"{ \
\"name\":\"sumu\", \
\"age\": 28, \
\"weight\": 69.8, \
\"address\": \
{ \
\"country\": \"China\",\
\"city\": \"shagnhai\",\
\"zip-code\": 999999\
}, \
\"skill\": [\"c\", \"html\", \"Python\"],\
\"student\": false \
}";2.2 代码编写
#include <stdio.h>
#include "../cJSON/cJSON.h"
char *JSON_data =
"{ \
\"name\":\"sumu\", \
\"age\": 28, \
\"weight\": 69.8, \
\"address\": \
{ \
\"country\": \"China\",\
\"city\": \"shagnhai\",\
\"zip-code\": 999999\
}, \
\"skill\": [\"c\", \"html\", \"Python\"],\
\"student\": false \
}";
int main(void)
{
cJSON* root = 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_city = NULL;
cJSON* cjson_address_zipcode = NULL;
cJSON* cjson_skill = NULL;
cJSON* cjson_skill_item = NULL;
int skill_array_size = 0;
int i = 0;
cJSON* cjson_student = NULL;
/* 解析整段JSO数据 */
root = cJSON_Parse(JSON_data);
if(root == NULL)
{
printf("parse fail.\n");
return -1;
}
/* 依次根据名称提取JSON数据(键值对) */
cjson_name = cJSON_GetObjectItem(root, "name");
cjson_age = cJSON_GetObjectItem(root, "age");
cjson_weight = cJSON_GetObjectItem(root, "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(root, "address");
cjson_address_country = cJSON_GetObjectItem(cjson_address, "country");
cjson_address_city = cJSON_GetObjectItem(cjson_address, "city");
cjson_address_zipcode = cJSON_GetObjectItem(cjson_address, "zip-code");
printf("address-country:%s\naddress-city:%s\naddress-zipcode:%d\n",
cjson_address_country->valuestring, cjson_address_city->valuestring, cjson_address_zipcode->valueint);
/* 解析数组 */
cjson_skill = cJSON_GetObjectItem(root, "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(root, "student");
if(cjson_student->valueint == 0)
{
printf("student: false\n");
}
else
{
printf("student:error\n");
}
return 0;
}2.3 编译源码
gcc cJSON.c main.c -o cJSON_test2.4 运行测试
sumu@sumu-virtual-machine:~/7Linux/cJSON-master$ ./cJSON_test
name: sumu
age:28
weight:69.8
address-country:China
address-city:shagnhai
address-zipcode:999999
skill:[c,html,Python]
student: false参考资料
