目录
一、JSON简介
JSON(JavaScript object Notation),即JavaScript 对象符号,是一种轻量级的数据格式;【其本质就是字符串】
它采用完全独立于编程语言的文本格式来存储和表示数据,语法简洁,层次结构清晰,易于人阅读和编写,同时也易于机器解析和生成,有效的提升了网络传送效率。
因为它完全独立于编程语言所以支持跨平台开发;
JSON 的名称中虽然带有JavaScript,但这是指其语法规则是参考JavaScript对象的,而不是指只能用于JavaScript 语言。
因为JSON本身就是参考JavaScript 对象的规则定义的,其语法与JavaScript定义对象的语法几乎完全相同。
二、 JSON语法
JSON可以将数据转换为字符串,接着在网络中或者程序里面传递这个字符串,最后可以还原成各个编程语言所支持的数据格式;
JSON是一个无序的键值对(key / value)集合;
JSON以"{“开始,以”}"结束,允许嵌套使用
每个key(关键字)和value(值)成对出现,关键字和值之间使用":"分隔
键/值对之间用","分隔
在这些字符前后允许存在无意义的空白符
数组(Array)用方括号(“[]”)表示。
对象(0bject)用大括号(“{}”)表示。
名称/值对(name/value)组合成数组和对象。
名称(name)置于双引号中,值(value)有字符串、数值、布尔值、null、对象和数组。
并列的数据之间用逗号(“,”)分隔
-
在线的JSON解析网站如下:JSON解析
-
如下是一个实际的例子,来体会一下JSON:
{
"name": "JK",
"age": 18,
"height": 175,
"sex": false,
"address": {
"country": "China",
"tel": 123456
},
"subject": ["语文", "数学", "英语"],
"grade": [1, 2, 3],
"student": [{
"name": "wsf",
"age": 23,
"sex": true
},
{
"name": "rs",
"age": 25,
"sex": true
}
]
}
-
JSON树可视化
三、 CJSON简介:
-
CJSON对象的实现采用了树形结构,每一个对象就是树的一个节点,每个节点由cJSON这个结构体组成,对象中的元素也由cJSON这个结构体组成。同一层的对象和元素是双向链表结构,由next和prev指针链接。不同层的对象或元素由child指针链接起来。type表示对象或元素类型,string表示对象或节点的名称。元素的值存储在valuestring, valueint和valuedouble中;
-
cJSON是一个使用C语言编写的JSON数据解析器,具有超轻便,可移植,单文件的特点,使用MIT开源协议。
-
要使用CJSON,直接把项目从Github上clone下来,接着把CJSON.h,CJSON.c这两个源文件加入到工程中就行,使用时include CJSON.h头文件就行;
四、 CJSON数据结构
-
cJSON使用cJSON结构体来表示一个JSON数据,定义在cJSON.h中,源码如下:
//逻辑值的宏定义
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_Raw (1 << 7) /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
/* 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;
首先,CJSON不是将一整段JSON数据全抽象出来,而是将其中的一条JSON数据抽象出来,也就是一个键值对,用上面的结构体 strcut cJSON 来表示,其中用来存放值的成员列表如下:
String:用于表示该键值对的名称;
type:用于表示该键值对中值的类型;
valuestring:如果键值类型(type)是字符串,则将该指针指向键值;
valueint:如果键值类型(type)是整数,则将该指针指向键值;
valuedouble:如果键值类型(type)是浮点数,则将该指针指向键值;
一段完整的JSON数据中由很多键值对组成,并且涉及到键值对的查找、删除、添加,所以使用链表来存储整段JSON数据,如上面的代码所示:
next指针:指向下一个键值对
prev指针指向上一个键值对
因为JSON数据支持嵌套,所以一个键值对的值会是一个新的JSON数据对象(一条新的链表),也有可能是一个数组,方便起见,在cJSON中,数组也表示为一个数组对象,用链表存储,所以:在键值对结构体中,当该键值对的值是一个嵌套的JSON数据或者一个数组时,由child指针指向该条新链表;
链表中的一些概念:
头指针:指向链表头结点的指针;
头结点:不存放有效数据,方便链表操作;
首节点:第一个存放有效数据的节点;
尾节点:最后一个存放有效数据的节点;
五、使用CJSON构造JSON
1. cJSON常用库函数介绍
extern cJSON * cJSON_CreateObject(void); //创建对象
extern cJSON * cJSON_CreateArray(void); //创建数组
extern cJSON * cJSON_CreateIntArray(const int *numbers, int count);//创建整型数组
extern cJSON * cJSON_CreateDoubleArray(const double *numbers, int count);//创建双浮点型数组
extern cJSON* cJSON_AddNullToObject(cJSON * const object, const char * const name);//在对象中添加null
extern cJSON* cJSON_AddTrueToObject(cJSON * const object, const char * const name);//在对象中添加true
extern cJSON* cJSON_AddFalseToObject(cJSON * const object, const char * const name);//在对象中添加false
extern cJSON* cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);//在对象中添加数字
extern cJSON* cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);//在对象中添加字符串
extern cJSON_bool cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); //在对象中添加项目
extern cJSON_bool cJSON_AddItemToArray(cJSON *array, cJSON *item);//在数组中添加项目
extern char * cJSON_Print(const cJSON *item);//JSON数据结构转换为JSON字符串---有格式
extern char * cJSON_PrintUnformatted(const cJSON *item); //JSON数据结构转换为JSON字符串---无格式
extern void cJSON_Delete(cJSON *item); //清除结构体
2. CJSON构造JSON实例代码
#include "string.h"
#include "cJSON.h"
#include "stdio.h"
int main()
{
// 创建头指针
cJSON *JSON_Root = NULL;
// 创建头结点,并将头指针指向头结点(可以看做是一个树的根root)
JSON_Root = cJSON_CreateObject();
// 添加一条字符串类型的JSON数据
cJSON_AddStringToObject(JSON_Root, "name", "zhuhua");
// 添加一条浮点类型的JSON数据(添加一个链表结点)到root下
cJSON_AddNumberToObject(JSON_Root, "height", 66.6666);
// 添加一条整数型的JSON数据(链表结点)
cJSON_AddNumberToObject(JSON_Root, "grade", 100);
// 添加一个嵌套的JSON数据(添加一个链表节点, 又一个《对象》)
cJSON *timeCode = cJSON_CreateObject();
// true
cJSON_AddTrueToObject(timeCode, "Sex");
cJSON_AddStringToObject(timeCode, "datatime", "20220811");
cJSON_AddFalseToObject(timeCode, "Judge");
// 最后把这个对象加入到root下
cJSON_AddItemToObject(JSON_Root, "TIMECODE", timeCode);
// 再次嵌套,加入一个数组类型的JSON数据,在timecode结点之下(添加一个链表结点)
cJSON *dataCode = cJSON_CreateArray();
// 两种方式添加
cJSON_AddStringToObject(dataCode, "", "English");
cJSON_AddItemToArray(dataCode, cJSON_CreateString("zhuzhuzhu"));
cJSON_AddStringToObject(dataCode, "", "huahuahua");
cJSON_AddNumberToObject(dataCode, "number", 300);
// 将datacode (array)结点添加到timecode 对象结点下(同样数组也可以有对象数据, 甚至数组中数据类型也可以不一样,但这样理解上不太好)
cJSON_AddItemToObject(timeCode, "DATACODE", dataCode);
// 最后将JSON数据结构转换为字符串, 并且输出
char *strForJson = cJSON_Print(JSON_Root);
printf("json字符串:%s\n", strForJson);
// 将字符串存入到文件中
FILE *FCJ = NULL;
FILE *FCJ1 = NULL;
FCJ = fopen("data.txt", "w");
FCJ1 = fopen("data1.txt", "w");
int i = 0;
for (i = 0; i < strlen(strForJson); ++i)
{
fprintf(FCJ, "%c", strForJson[i]);
fputc(strForJson[i], FCJ1);
}
fclose(FCJ);
fclose(FCJ1);
// 清除结构体
cJSON_Delete(JSON_Root);
return 0;
}
-
输出结果如下:
(1)!!!编译过程中有两个注意的点:
-
提示头文件中报unknown type name ‘size_t’错误:
-
原因及解决方案:
-
原因在测试c文件中加入了string.h的头文件,并且放在了cJSON.h的后面;
-
解决:一定要把string.h放在cJSON头文件的前面;
-
-
提示找不到pow和floor函数:
-
解决方案:需要链接math库libm(可以查一下libc相关)
-
编译语句
gcc cJson.c CJSONTest.c -o uim -lm
最后./uim执行即可;
3. 使用cJSON解析JSON实例代码
#include <stdio.h>
#include "cJSON.h"
int main()
{
// 定义JSON字符串
char json_string[]="{\"name\":\"zhuhua\",\
\"height\":66.666600,\"grade\":100,\"TIMECODE\":{\"Sex\":true,\"datatime\":\"20220811\",\"Judge\":false,\"DATACODE\":[\"English\",\"zhuzhuzhu\",\"huahuahua\",300]}}";//定义JSON字符串
// 将JSON字符串转换成JSON结构体
cJSON *JSON_ROOT = cJSON_Parse(json_string);
// 判断转换是否成功
if (JSON_ROOT == NULL)
{
printf("cjson error.......\n");
}
else
{
printf("%s\n", cJSON_Print(JSON_ROOT));
}
printf("提取数据\n");
// 解析字符串 找key 得value
char *name = cJSON_GetObjectItem(JSON_ROOT, "name")->valuestring;
// 打印看一下效果
printf("%s\n", name);
double height = cJSON_GetObjectItem(JSON_ROOT, "height")->valuedouble;
printf("%lf\n", height);
int grade = cJSON_GetObjectItem(JSON_ROOT, "grade")->valueint;
printf("%d\n", grade);
// 对象和数组的接收
cJSON *obj_timeCode = cJSON_GetObjectItem(JSON_ROOT, "TIMECODE"); // 解析对象
cJSON *arr_dataCode = cJSON_GetObjectItem(obj_timeCode, "DATACODE"); // 解析数组 一层一层来
int sex = cJSON_GetObjectItem(obj_timeCode, "Sex")->type; // 解析逻辑值————输出逻辑值对应的宏定义数值
printf("%d\n", sex);
char *datatime = cJSON_GetObjectItem(obj_timeCode, "datatime")->valuestring;
printf("%s\n", datatime);
int judge = cJSON_GetObjectItem(obj_timeCode, "Judge")->type; // 解析逻辑值————输出逻辑值对应的宏定义数值
printf("%d\n", judge);
// 数组的处理 先获取数组的成员个数
int arr_size = cJSON_GetArraySize(arr_dataCode);
printf("arrsize: %d\n", arr_size);
int i = 0;
for (i = 0; i < arr_size - 1; ++i)
{
printf("%s\n", cJSON_GetArrayItem(arr_dataCode, i)->valuestring);
}
printf("%d\n", cJSON_GetArrayItem(arr_dataCode, arr_size - 1)->valueint);
// 清除结构体
cJSON_Delete(JSON_ROOT);
return 0;
}
-
运行结果如下