文章目录
- 前言
- 一、JSON介绍
- 使用json的优点
- JSON 的不足
- JSON 存储
- 使用JSON的时机
- 二、JSON的语言介绍
- 数据类型
- 三、c/c++解析JSON数据
- cJSON介绍
- cJSON存储数据
- cJSON的api函数的使用
- 解析JSON
前言
因项目中是选用json,传输网络数据。因此要解析json数据。所以前来学习
一、JSON介绍
JSON是一种轻量级的、基于文本的、开放的数据交换格式。它本身不提供任何方法(函数),非常适合在网络中进行传输。
其中,在 JSON 中,使用以下两种方式来表示数据:
(1)Object(对象):键/值对(名称/值)的集合,使用花括号{ }定义。在每个键/值对中,以键开头,后跟一个冒号:,最后是值。多个键/值对之间使用逗号,分隔。
(2)Array(数组):值的有序集合,使用方括号[ ]定义,数组中每个值之间使用逗号,进行分隔。
如下所示:
{
"Name":"C语言中文网",
"Url":"http://c.biancheng.net/",
"Tutorial":"JSON",
"Article":[
"JSON 是什么?",
"JSONP 是什么?",
"JSON 语法规则"
]
}
使用json的优点
JSON 并不是唯一能够实现在互联网中传输数据的方式,除此之外还有一种 XML 格式。但是JSON 比 XML 的可读性更高,而且 JSON 更加简洁,更容易理解。
与 XML 相比,JSON 具有以下优点:
(1)结构简单、紧凑:与 XML 相比,JSON 遵循简单、紧凑的风格,有利于程序员编辑和阅读,而 XML 相对比较复杂;
(2)更快:JSON 的解析速度比 XML 更快(因为 XML 与 HTML 很像,在解析大型 XML 文件时需要消耗额外的内存),存储同样的数据,JSON 格式所占的存储空间更小;
(3)可读性高:JSON 的结构有利于程序员阅读。
JSON 的不足
(1)只有一种数字类型:JSON 中只支持 IEEE-754 双精度浮点格式,因此无法使用 JSON 来存储许多编程语言中多样化的数字类型;
(2)没有日期类型:在 JSON 中只能通过日期的字符串(例如:1970-01-01)或者时间戳(例如:1632366361)来表示日期;
(3)没有注释:在 JSON 中无法添加注释;
(4)冗长:虽然 JSON 比 XML 更加简洁,但它并不是最简洁的数据交换格式,对于数据量庞大或用途特殊的服务,您需要使用更加高效的数据格式。
JSON 存储
JSON 数据可以存储在 .json 格式的文件中(与 .txt 格式类似,都属于纯文本文件),也可以将 JSON 数据以字符串的形式存储在数据库、Cookie、Session 中。
使用JSON的时机
(1)定义接口
前后端分离,后端返回的数据;
开发 API,例如百度、高德的一些开放接口。
RPC 远程调用;
(2)序列化
序列化是将程序中的对象直接转换为可保存或者可传输的数据,但这样会保存对象的类型信息,无法做到跨语言使用,例如我们使用 Python 将数据序列化到硬盘,然后使用 Java 来读取这份数据,这时由于不同编程语言的数据类型不同,就会造成读取失败。如果在序列化之前,先将对象信息转换为 JSON 格式,则不会出现此类问题。
二、JSON的语言介绍
数据类型
1.字符串
JSON 中的字符串需要使用双引号定义(注意:不能使用单引号),字符串中可以包含零个或多个 Unicode 字符。另外,JSON 的字符串中也可以包含一些转义字符。代码实例如下:
{
"name":"C语言中文网",
"url":"http://c.biancheng.net/",
"title":"JSON 数据类型"
}
2.数字
JSON 中不区分整型和浮点型,只支持使用 IEEE-754 双精度浮点格式来定义数字。此外,JSON 中不能使用八进制和十六进制表示数字,但可以使用 e 或 E 来表示 10 的指数。代码实例如下:
{
"number_1" : 210,
"number_2" : -210,
"number_3" : 21.05,
"number_4" : 1.0E+2
}
3.布尔值
JSON 中的布尔值与编程语言中相似,有两个值,分别为 true(真)和 false(假)。代码实例如下:
{
"message" : true,
"pay_succeed" : false
}
4.空
null(空)是 JSON 中的一个特殊值,表示没有任何值,当 JSON 中的某些键没有具体值时,就可以将其设置为 null。代码实例如下:
{
"id" : 1,
"visibility" : true,
"popularity" : null
}
5.对象
在 JSON 中,对象是一个无序的 、键/值对的集合,一个对象以左花括号{开始,以右花括号}结束,左右花括号之间为对象中的若干键/值对。键/值对中,键必须是字符串类型(即使用双引号将键包裹起来),而值可以是 JSON 中的任意类型,键和值之间需要使用冒号:分隔开,不同的键/值对之间需要使用逗号,分隔开。代码实例如下:
{
"author": {
"name": "C语言中文网",
"url": "http://c.biancheng.net/"
}
}
6.数组
数组是值的有序集合,JSON 中的数组需要使用方括号[ ]定义,方括号中为数组中的若干值,值可以是 JSON 中支持的任意类型(例如字符串、数字、布尔值、对象、数组等),每个值之间使用逗号,分隔开。代码实例如下:
{
"array":[
{
"name":"C语言中文网",
"url":"http://c.biancheng.net/",
"course":"JSON教程"
},
[
"JSON是什么?",
"JSON语法规则",
"JSON数据类型"
],
"JSON",
18,
true
]
}
三、c/c++解析JSON数据
通过操作cJSON开源库(小巧玲珑,且是纯C的)来解析JSON字符串。去官网下载一下,把那个库的cJSON.c和cJSON.h文件拷贝到自己的项目中即可。接下来是对这个库的学习,并解析JSON数据
cJSON介绍
对于了解cJSON,其实主要了解下cJSON数据存储,以及API即可。毕竟,我们学到还是为了用。
cJSON存储数据
cJSON主要是通过如下结构体cJSON进行存储数据:
typedef struct cJSON {
struct cJSON *next,*prev; /* next是获取下一个元素数据,prev是获取前一个元素数据 */
struct cJSON *child; /* 获取第一个元素数据,当需要获取下一个时,就得使用next了. */
int type; /* 当前的json类型对象、数组、字符串、数字、null、true、false等 */
char *valuestring; /* 字符串值, if type==cJSON_String */
int valueint; /* 整形类型值, if type==cJSON_Number */
double valuedouble; /* 浮点数类型值, if type==cJSON_Number */
char *string; /* 这个是键 */
} cJSON;
其中,type类型,与下面的宏进行判断
/* cJSON Types: */
#define cJSON_False 0 // true
#define cJSON_True 1 // false
#define cJSON_NULL 2 // NULL
#define cJSON_Number 3 // 数字
#define cJSON_String 4 // 字符串
#define cJSON_Array 5 // 数组
#define cJSON_Object 6 // 对象
cJSON的api函数的使用
1.cJSON对象的创建,元素的插入
// 定义对象 { }
cJSON *interest = cJSON_CreateObject();
// 插入元素,对应 键值对
cJSON_AddItemToObject(interest, "basketball", cJSON_CreateString("篮球"));// 当值是字符串时,需要使用函数cJSON_CreateString()创建
cJSON_AddItemToObject(interest, "badminton", cJSON_CreateString("羽毛球"));
// 或者使用宏进行添加
//cJSON_AddStringToObject(interest, "badminton", "羽毛球"); // 或者这样写
实现效果如下:
"interest": {
"basketball": "篮球",
"badminton": "羽毛球"
}
// 定义 [ ] 数组
cJSON *color = cJSON_CreateArray();
// 往数组中添加元素
cJSON_AddItemToArray(color, cJSON_CreateString("black"));
cJSON_AddItemToArray(color, cJSON_CreateString("white"));
实现效果如下:
"color": [ "black", "white"]
// 定义 { } 对象
cJSON *likeObject1 = cJSON_CreateObject();
cJSON_AddItemToObject(likeObject1, "game", cJSON_CreateString("马里奥"));
cJSON_AddItemToObject(likeObject1, "price", cJSON_CreateNumber(66.6)); // 当值是数字时,需要使用函数cJSON_CreateNumber()创建
//cJSON_AddNumberToObject(likeObject1, "price", 66.6); // 或者这样写
cJSON *likeObject2 = cJSON_CreateObject();
cJSON_AddItemToObject(likeObject2, "game", cJSON_CreateString("魂斗罗"));
cJSON_AddItemToObject(likeObject2, "price", cJSON_CreateNumber(77.7));
// 定义 [ ] 数组
cJSON *like = cJSON_CreateArray();
// 往数组中添加元素
cJSON_AddItemToArray(like, likeObject1);
cJSON_AddItemToArray(like, likeObject2);
实现效果如下:
"like": [
{ "game": "马里奥", "price": 66.6 },
{ "game": "魂斗罗", "price": 77.7 }
]
// 定义 [ ] 数组
cJSON *education1 = cJSON_CreateArray();
cJSON_AddItemToArray(education1, cJSON_CreateString("小学"));
cJSON_AddItemToArray(education1, cJSON_CreateString("初中"));
cJSON *education2 = cJSON_CreateArray();
cJSON_AddItemToArray(education2, cJSON_CreateString("高中"));
cJSON_AddItemToArray(education2, cJSON_CreateString("大学"));
// 定义 [ ] 数组
cJSON *education = cJSON_CreateArray();
cJSON_AddItemToArray(education, education1);
cJSON_AddItemToArray(education, education2);
实现效果如下:
"education": [
[ "小学", "初中" ],
[ "高中", "大学" ]
]
2.字符串生成cjson指针的函数,使用后需要调用cJSON_Delete进行释放
extern cJSON *cJSON_Parse(const char *value);
// 释放cJSON_Parse返回的指针
extern void cJSON_Delete(cJSON *c);
3.cjson指针指针生成字符串的函数
// 这个生成的字符串有做格式调整
extern char *cJSON_Print(cJSON *item);
// 这个没有作格式调整,就是一行字符串显示
extern char *cJSON_PrintUnformatted(cJSON *item);
使用这个两个函数一定一定一定要释放它们返回的指针内存,否则会造成内存泄漏。
如下演示:
// 打印控制台查看
char *cPrint = cJSON_Print(root);
char *cPrintUnformatted = cJSON_PrintUnformatted(root);
printf("cJSON_Print:\n%s\n", cPrint); // cJSON_Print:有做格式调整
printf("cJSON_PrintUnformatted:\n%s\n", cPrintUnformatted); // cJSON_PrintUnformatted:没有做格式调整
// 返回的字符串指针需要自己释放
free(cPrint);
free(cPrintUnformatted);
解析JSON
下面解析会提供两种方式进行解析,第一种是固定的,写死的方式;第二种是灵活的的方式解析!
1.打开文件读取josn数据
// 打开文件
FILE *file = NULL;
file = fopen(FILE_NAME, "r");
if (file == NULL) {
printf("Open file fail!\n");
return;
}
// 获得文件大小
struct stat statbuf;
stat(FILE_NAME, &statbuf);
int fileSize = statbuf.st_size;
printf("文件大小:%d\n", fileSize);
// 分配符合文件大小的内存
char *jsonStr = (char *)malloc(sizeof(char) * fileSize + 1);
memset(jsonStr, 0, fileSize + 1);
// 读取文件中的json字符串
int size = fread(jsonStr, sizeof(char), fileSize, file);
if (size == 0) {
printf("读取文件失败!\n");
fclose(file);
return;
}
printf("%s\n", jsonStr);
fclose(file);
2.使用读取到的json数据初始化cJSON指针
// 将读取到的json字符串转换成json变量指针
cJSON *root = cJSON_Parse(jsonStr);
if (!root) {
printf("Error before: [%s]\n", cJSON_GetErrorPtr());
free(jsonStr);
return;
}
free(jsonStr);
3.定义一些下面需要使用到的变量
cJSON *item = NULL;
char *v_str = NULL;
double v_double = 0.0;
int v_int = 0;
bool v_bool = false;
4.直接通过键进行解析的
解析时需要使用结构体中的type类型进行判断,这是为了安全性考虑!
解析时也可以使用cJSON_Print函数去获取字符串或者使用结构体中的valuestring进行获取,但是要注意的是,使用cJSON_Print函数去获取字符串需要free掉获取到的指针,否则会造成内存泄漏!
// 解析:"name": "小明",
item = cJSON_GetObjectItem(root, "name");
if (item != NULL) {
/* 写法一:*/
// 判断是不是字符串类型
//if (item->type == cJSON_String) {
// v_str = cJSON_Print(item); // 通过函数获取值
// printf("name = %s\n", v_str);
// free(v_str); // 通过函数返回的指针需要自行free,否则会导致内存泄漏
// v_str = NULL;
//}
/* 写法二: */
// 判断是不是字符串类型
if (item->type == cJSON_String) {
v_str = item->valuestring; // 此赋值是浅拷贝,不需要现在释放内存
printf("name = %s\n", v_str);
}
}
// 解析:"age": "23",
item = cJSON_GetObjectItem(root, "age");
if (item != NULL) { // 合法性检查
if (item->type == cJSON_Number) { // 判断是不是数字
v_int = item->valueint; // 获取值
printf("age = %d\n", v_int);
}
}
// 解析:"vip": true,
item = cJSON_GetObjectItem(root, "vip");
if (item != NULL) {
if (item->type == cJSON_True || item->type == cJSON_False) {
v_str = cJSON_Print(item); // 由于bool类型结构体中没有给出,所以使用字符串代替
printf("vip = %s\n", v_str);
free(v_str);
v_str = NULL;
}
}
// 解析:"address": null
item = cJSON_GetObjectItem(root, "address");
if (item != NULL && item->type == cJSON_NULL) {
v_str = cJSON_Print(item); // 由于NULL类型结构体中没有给出,所以使用字符串代替
printf("address = %s\n", v_str);
free(v_str);
v_str = NULL;
}
本项目中,主要是使用直接通过键进行解析的,学到这已经能够完成项目的设计了。
对于其他的,比如解析数组啊,解析对象啊,之类的操作,等有时间用到了再来补充。毕竟还要按照计划时间来完成项目的学习。