目录
了解json
JSON: JavaScript 的对象表示法( JavaScript Object Notation)。是一种轻量级的数据交换格式。
JSON采用完全独立于语言的文本格式, 但是也使用了类似于C语言家族的习惯( 包括C、 C++、 C#、 Java、 JavaScript、 Perl、 Python等)。JSON 具有自我描述性, 语法简洁, 易于理解。这些特性使JSON成为理想的数据交换语言。 易于人阅读和编写, 同时也易于机器解析和生成(一般用于提升网络传输速率)。
JSON 解析器和 JSON 库支持许多不同的编程语言。 JSON 文本格式在语法上与创建 JavaScript 对象的代码相同。 由于这种相似性, 无需解析器, JavaScript 程序能够使用内建的 eval() 函数, 用 JSON 数据来生成原生的 JavaScript 对象,而在C语言中我们一般使用cJSON解析器来进行json字符串的解析。
json数据类型
- number:和JavaScript的number完全一致;相当于C中的int类型
- boolean:就是JavaScript的true或false;相当于c++中的bool类型
- string:就是JavaScript的string;相当于c++的string类型
- null:就是JavaScript的null;相当于C的NULL类型
- array:就是JavaScript的Array表示方式——[];相当于C的数组
- object:就是JavaScript的{ ... }表示方式。相当于C++的类或者C的结构体
注:
1.json的数据类型在源码实现中和具体的编程语言有关,比如boolean在C中并没有相应的类型,C相关的实现库可能会用0和1表示。
2.number的数据虽然是相当于C中的int类型,但是也可以存放浮点型数据等内容。
格式规范
- json以大括号起始和结尾
- 内容都是以键值对的形式存在
- 所有的键都是字符串
- 值的类型不一定,属于JavaScript 的基本数据类型
- 每个键值对以
,
分割 - 最后一个键值对不加逗号
json格式校验
JSON基本操作
Json序列化:可以理解为利用程序生成Json字符串的过程。
Json反序列化:可以理解为利用程序将已有的Json字符串解析出我们需要的值的过程。
JSON解析(反序列化)
cJSON解析器对象的实现采用了树形结构,每个对象是树的一个节点,每个节点由cJSON这个结构体组成,对象中的元素也由cJSON这个结构体组成。同一层的对象和元素是双向链表结构,由next和prev指针链接。不同层的对象或元素由child指针链接起来。type表示对象或元素类型,string表示对象或节点的名称。元素的值存储在valuestring, valueint和valuedouble中,cJSON.h中有详细的注释。解析json我们一般借助以下函数:
cJSON *cJSON_Parse(const char *value);
//功能:将一个JSON数据包,按照cJSON结构体的结构序列化整个数据包,并在堆中开辟一块内存存储cJSON结构体
//返回值:成功返回一个指向内存块中的cJSON的指针,失败返回NULL
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
//功能:获取JSON字符串字段值
//返回值:成功返回一个指向cJSON类型的结构体指针,失败返回NULL
int cJSON_GetArraySize(cJSON *array);
//功能:获取数组成员对象个数
//返回值:数组成员对象个数
int cJSON_HasObjectItem(cJSON *object,const char *string)
//功能:判断cjson对象中是否有某键
//返回值:如果有返回1 否则返回0
cJSON *cJSON_GetArrayItem(cJSON *array,int item);
//根据下标获取cjson对象数组中的对象
void cJSON_Delete(cJSON *c);
/*作用:释放位于堆中cJSON结构体内存
返回值:无*/
char *cJSON_GetErrorPtr(void);
//获取错误字符串
char *cJSON_Print(cJSON *item);
//从cjson对象中获取有格式的json对象,打印到终端
char *cJSON_PrintUnformatted(cJSON *item);
//从cjson对象中获取无格式的json对象,打印到终端
JSON构造(序列化)
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
//创建对象---常用
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
//创建数组---常用
CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
//创建整型数组
CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
//创建双浮点型数组
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
//在对象中添加null
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
//在对象中添加true
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
//在对象中添加false
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_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
//在对象中添加项目
CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
//在数组中添加项目
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
//JSON数据结构转换为JSON字符串---有格式
CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
//JSON数据结构转换为JSON字符串---无格式
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
//清除结构体
练习
1.对以下json字符串进行序列化。
2.使用cJSON库对下面的将下面内容存为json文件,读取后解析,进行反序列化操作。
{
"ver": "1.0",
"login": {
"user": "zhangsan",
"key": "123456"
},
"data": [{
"key": 1,
"type": 2,
"val": "10"
},
{
"key": 2,
"type": 1,
"val": "0"
},
{
"key": 3,
"type": 3,
"val": "22.5"
}
]
}
序列化例程:
// 1.构造根节点root
cJSON * root = cJSON_CreateObject();
// 2.给 root 添加 ver 的子节点,子节点类型是 string
cJSON_AddItemToObject(root, "ver", cJSON_CreateString("1.0"));
// 3.给 root 添加 login 的子节点,子节点类型是 object
cJSON * object1 = cJSON_CreateObject();
cJSON_AddItemToObject(root, "login", object1);
// 4.给 login 节点添加 user 和 key 节点
cJSON_AddItemToObject(object1, "user", cJSON_CreateString("zhangsan"));
cJSON_AddItemToObject(object1, "key", cJSON_CreateString("123456"));
// 5.创建数组节点
cJSON *array = cJSON_CreateArray();
// 6.将数组data加入根节点中
cJSON_AddItemToObject(root, "data", array);
// 7.往data数组节点遍历增加项
list_for_each(p, &head)
{
tmp = list_entry(p, struct data, list);
// 7.1 创建数组data的子节点(此时还没放进数组data中)
cJSON *tmp_son = cJSON_CreateObject();
// 7.2 往数组data字节点中增加key和type
cJSON_AddItemToObject(tmp_son, "key", cJSON_CreateNumber(tmp->key));
cJSON_AddItemToObject(tmp_son, "type", cJSON_CreateNumber(tmp->type));
// 7.3 往数组data字节点中增加val
// 7.3.1 处理val
char buf_tmp[32] = "";
switch (tmp->type)
{
case 1:
sprintf(buf_tmp, "%d", tmp->val.b);
break;
case 2:
sprintf(buf_tmp, "%d", tmp->val.i);
break;
case 3:
sprintf(buf_tmp, "%0.2f", tmp->val.f);
break;
}
// 7.3.2 将处理好的val即string基本类型添加到数组data的字节点中
cJSON_AddItemToObject(tmp_son, "val", cJSON_CreateString(buf_tmp));
// 7.3.3 将字节点tmp_son增加到数组data中
cJSON_AddItemToArray(array, tmp_son);
}
// 8.打印cJSON验证
printf("\n打印Json字符串验证:\n");
printf("%s\n", cJSON_Print(root));
反序列化例程:
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
#include "cJSON.h"
#include "cJSON.c"
typedef int BOOL;
union val_t {
BOOL b;
int i;
float f;
};
struct data
{
int key;
int type;
union val_t val;
struct list_head list;
};
int main(int argc, char const *argv[])
{
/* ----------读取文件---------- */
FILE *fd = NULL;
fd = fopen("./test.json", "r");
if (NULL == fd)
{
perror("fopen failed.");
return -1;
}
char buf[500] = "";
if (fread(buf, 500, 1, fd) < 0)
{
perror("fread failed.");
return -1;
}
/* -----------JSON的反序列化----------- */
// 1.取出对象object
cJSON *object = cJSON_Parse(buf);
if (NULL == object)
{
printf("Error before: [%s]\n", cJSON_GetErrorPtr());
return -1;
}
// 2.取出对象object下的节点ver和login
cJSON *item = cJSON_GetObjectItem(object, "ver");
printf("ver: %s\n", item->valuestring);
cJSON *item_login = cJSON_GetObjectItem(object, "login");
// 3.从login节点中取出user和key两子节点
item = cJSON_GetObjectItem(item_login, "user");
printf("user: %s\n", item->valuestring);
item = cJSON_GetObjectItem(item_login, "key");
printf("key: %s\n", item->valuestring);
// 4.取出对象object下的数组节点data
item = cJSON_GetObjectItem(object, "data");
// 5.链表初始化
struct list_head head;
INIT_LIST_HEAD(&head);
// 6.遍历取出数组中每个项并加到链表里面去
for (int i = 0; i < cJSON_GetArraySize(item); i++)
{
// 6.1 根据下标从数组节点中取出第i个节点
cJSON *value = cJSON_GetArrayItem(item, i);
// 6.2 从第i个节点中取子节点key和type
cJSON *value_key = cJSON_GetObjectItem(value, "key");
cJSON *value_type = cJSON_GetObjectItem(value, "type");
// 6.3 malloc出结构体指针node
struct data *node = (struct data *)malloc(sizeof(struct data));
// 6.4 通过指针对结构体赋值
node->key = value_key->valueint;
node->type = value_type->valueint;
cJSON *value_val = cJSON_GetObjectItem(value, "val");
switch (value_type->valueint)
{
case 1:
node->val.b = atoi(value_val->valuestring);
break;
case 2:
node->val.i = atoi(value_val->valuestring);
break;
case 3:
node->val.f = atof(value_val->valuestring);
break;
}
// 7.加到链表里由链表存储
list_add(&node->list, &head);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~遍历输出链表,验证已经由链表存储~~~~~~~~~~~~~~~~~~~~~~~ */
printf("\n遍历输出链表, 验证已经由链表存储:\n");
struct list_head *p;
struct data *tmp;
list_for_each(p, &head)
{
tmp = list_entry(p, struct data, list);
printf("key = %d, type = %d, ", tmp->key, tmp->type);
switch (tmp->type)
{
case 1:
printf("val = %d\n", tmp->val.b);
break;
case 2:
printf("val = %d\n", tmp->val.i);
break;
case 3:
printf("val = %0.2f\n", tmp->val.f);
break;
}
}
return 0;
}
注意事项:
编译cJSON库时候,gcc需要增加 -l m选项,动态链接math库。
![](https://i-blog.csdnimg.cn/blog_migrate/3161775a084271c3f1ef5622a0c47b40.jpeg)