一文搞懂JSON

一文搞懂JSON

目录

什么是JSON?

JSON的基本数据类型

JSON的特点和优势(了解)

JSON格式规范(重点)

JSON的基本操作

关键接口的梳理

序列化

反序列化

答案和解析

序列化答案

反序列化答案

第一种思路

第二种思路

什么是JSON?

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,常用于将数据结构化地传输和存储。它由键值对组成,采用类似于JavaScript对象的格式来表示数据。JSON易于阅读和编写,并且易于解析和生成,成为广泛应用于Web应用程序和数据交换的标准格式之一。

JSON的基本数据类型

类型

解释

案例

字符

(String)

由双引号包围的Unicode字符序列

"Hello, World!"

数字(Number)

包括整数和浮点数

42,3.14

布尔值(Boolean)

表示真或假,C相关的实现库可能会用0和1表示

值为true或false

空值

(Null)

表示空值

值为null

对象

(Object)

由一组无序的键值对组成,键是字符串,值可以是任意的JSON数据类型。键值对之间使用逗号分隔,整个对象使用花括号{}包围

{"name": "John",

"age": 30,

"city": "New York"}

数组

(Array)

由一组有序的值组成,值可以是任意的JSON数据类型。值之间使用逗号分隔,整个数组使用方括号[]包围

[1, 2, 3, 4, 5]

JSON的特点和优势(了解)

简洁和易读:JSON使用简洁的文本格式表示数据,易于阅读和编写。平台无关性:JSON是一种独立于编程语言和平台的数据格式,可被多种编程语言解析和生成。易于解析和生成:JSON的解析和生成相对简单,各种编程语言都提供了相应的JSON解析器和生成器。支持复杂数据结构:JSON支持嵌套、复杂的数据结构,可以表示对象、数组和各种组合类型的数据。与Web应用程序兼容性良好:JSON广泛用于Web应用程序中,可以轻松地与JavaScript进行交互。

JSON格式规范(重点)

1. 数据使用键值对表示,键和值之间使用冒号(:)分隔。

例如:{"name": "John", "age": 30}

2. 键使用双引号(")包围,值可以是字符串、数字、布尔值、对象、数组或null。

例如:{"name": "John", "age": 30, "isStudent": true, "address": null}

3. 键值对之间使用逗号(,)分隔,最后一个键值对后不应有逗号。

例如:{"name": "John", "age": 30}

4. 字符串值使用双引号(")包围,可以包含任意Unicode字符序列,特殊字符可以使用转义字符表示。

例如:"Hello, World!", "I "love" JSON"

5. 数字可以是整数或浮点数,不使用引号包围。

例如:42, 3.14

6. 布尔值只有两个取值:true和false,不使用引号包围。

例如:true, false

7. 数组使用方括号([])包围,值之间使用逗号分隔。

例如:[1, 2, 3, 4, 5]

8. 对象使用花括号({})包围,键值对之间使用逗号分隔。

例如:{"name": "John", "age": 30}

9. JSON是严格区分大小写的,键和字符串值都应该使用双引号包围。

10. JSON可以嵌套,允许在对象中包含对象或数组,或在数组中包含对象或其他数组。

11. JSON不支持注释,不允许在JSON数据中添加注释。

下面是一个简单的JSON

{

"name": "southernbrid",

"age": 14,

"gender": true,

"height": 1.65,

"grade": null,

"skills": [

"JavaScript",

"Java",

"Python",

"Lisp"

]

}

分享个JSON格式校验网站

在线JSON校验格式化工具(Be JSON)

JSON的基本操作

JSON的基本操作通常涉及以下几个方面:

创建JSON对象: 可以使用编程语言提供的函数、类或库来创建JSON对象。通常,这些函数或方法接受键值对作为参数,用于指定JSON对象的属性和对应的值。解析JSON字符串: 将JSON字符串解析为相应的数据结构,如对象、数组或基本数据类型。编程语言提供相应的解析函数或方法,可以将JSON字符串转换为可操作的数据对象。生成JSON字符串: 将数据对象转换为JSON字符串的表示形式,以便于传输、存储或与其他系统进行交互。编程语言提供相应的函数或方法,可以将数据对象转换为符合JSON格式规范的字符串。访问和修改JSON对象的属性: 通过键访问JSON对象的属性,并可以对其进行修改。可以使用编程语言提供的API来访问、读取和修改JSON对象的属性值。遍历JSON数组: 遍历JSON数组中的元素,逐个访问和处理数组中的数据项。使用循环结构来遍历数组,根据索引或迭代器获取数组中的每个元素。嵌套JSON操作: 处理嵌套的JSON结构,包括访问、修改和操作嵌套的对象或数组。可以使用递归、循环等方法来处理嵌套的JSON结构。序列化和反序列化(重点): 将JSON对象序列化为字符串,或将JSON字符串反序列化为对象。序列化是将数据对象转换为JSON字符串,反序列化是将JSON字符串转换为数据对象。(本文主要介绍C语言的JSON库来进行序列化和反序列化)

下面是一个C库,用来完成本文教学

📎cJSON.chttps://www.yuque.com/attachments/yuque/0/2023/txt/35243076/1687951166715-b8398dec-d697-4507-b49e-1e2ea5595ce9.txt📎cJSON.hhttps://www.yuque.com/attachments/yuque/0/2023/txt/35243076/1687951166713-f66a28ef-8aaa-4fb0-8f7c-7fa49d94b608.txt

下面两篇文章也能帮助我们完成JSON序列化和JSON反序列化的操作

cJSON 使用详解_无痕眼泪的博客-CSDN博客由于c语言中,没有直接的字典,字符串数组等数据结构,所以要借助结构体定义,处理json。如果有对应的数据结构就方便一些, 如python中用json.loads(json)就把json字符串转变为内建的数据结构处理起来比较方便。一个重要概念:在cjson中,json对象可以是json,可以是字符串,可以是数字。。。cjson数据结构定义:#d..._cjsonhttps://blog.csdn.net/qq_32172673/article/details/88305781

https://www.cnblogs.com/liunianshiwei/p/6087596.htmlhttps://www.cnblogs.com/liunianshiwei/p/6087596.html

编译注意事项

编译cJSON库时候,gcc需要增加 -lm 选项,动态链接math库。

关键接口的梳理

/* 类型定义 */

#define cJSON_False 0

#define cJSON_True 1

#define cJSON_NULL 2

#define cJSON_Number 3

#define cJSON_String 4

#define cJSON_Array 5

#define cJSON_Object 6

/* CJSON核心结构体 */

typedef struct cJSON {

struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */

struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */

int type; /* 节点的类型,取值是上面的6种 */

char *valuestring; /* 如果类型是cJSON_String,那么值会被存到这里 */

int valueint; /* 如果类型是cJSON_Number,且是整型,那么值会被存到这里 */

double valuedouble; /* 如果类型是cJSON_Number,且是浮点型,那么值会被存到这里 */

char *string; /* 键*/

} cJSON;

/****************************通用接口****************************/

//把传入的字符串转成cJSON的结构(反序列化)

cJSON *cJSON_Parse(const char *value);

//把cJSON结构转成字符串(序列化),调用后需要配合free接口释放malloc的内存

char *cJSON_Print(cJSON *item);

//无格式化序列化,节省内存,调用后需要配合free接口释放malloc的内存

char *cJSON_PrintUnformatted(cJSON *item);

//释放节点的内存,防止内存泄露

void cJSON_Delete(cJSON *c);

/****************************反序列化相关接口****************************/

//获取数组的大小

int cJSON_GetArraySize(cJSON *array);

//从array数组中获取第item个子节点

cJSON *cJSON_GetArrayItem(cJSON *array,int item);

//获取object大节点名字叫string的子节点

cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);

//判断object大节点中是否有名字叫string的小节点

int cJSON_HasObjectItem(cJSON *object,const char *string);

/****************************序列化相关接口****************************/

//创建一个普通节点

cJSON *cJSON_CreateObject(void);

//创建一个数组节点

cJSON *cJSON_CreateArray(void);

//把item节点增加到array的数组节点中

void cJSON_AddItemToArray(cJSON *array, cJSON *item);

//把item节点增加到object中作为子节点,item节点的键名为string

void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);

//创建各种类型的cJSON节点

cJSON *cJSON_CreateNull(void);

cJSON *cJSON_CreateTrue(void);

cJSON *cJSON_CreateFalse(void);

cJSON *cJSON_CreateBool(int b);

cJSON *cJSON_CreateNumber(double num);

cJSON *cJSON_CreateString(const char *string);

cJSON *cJSON_CreateArray(void);

cJSON *cJSON_CreateObject(void);

序列化

使用cJSON库序列化出下面的JSON内容

{

"name": "小明",

"age": 14,

"gender": true,

"height": 1.65,

"grade": null,

"skills": [

"JavaScript",

"Java",

"Python",

"Lisp"

]

}

反序列化

使用cJSON库对下面的JSON文件进行反序列化操作。ver和login的子节点直接打印即可,data的数据节点解析后形成链表存储(将下面内容存为文件,读取后解析即可)。

{

"ver": "1.0",

"login": {

"user": "zhangsan",

"pwd": 1234

},

"data": [{

"key": 1,

"type": 2,

"val": "10"

},

{

"key": 2,

"type": 1,

"val": "0"

},

{

"key": 3,

"type": 3,

"val": "22.5"

}

]

}

注意:上面的所有val值都是string类型,解析出来存到节点前,需要根据type的类型来进行转换,按照相应的类型存储到共用体中。

提示:对于数据解析后,形成新的节点存放链表,最好的方式是为每个节点分配单独的内存。例:

struct data {

int key;

int type;

union val_t val;

struct list_head list;

};

struct data *node = (struct data *)malloc(sizeof(struct data));

node->xxx = xxx;

插入链表即可

答案和解析

序列化答案

#include

#include "cJSON.h"

#include

#include

#include

#include

#include

int main(int argc, char const *argv[])

{

// 序列化

cJSON *root = cJSON_CreateObject(); // 创建根节点

cJSON *age = cJSON_CreateNumber(14); // 创建一个数值类型的节点

cJSON_AddItemToObject(root, "age", age); // 将age节点添加到root对象中,键名为"age"

// 增加数组节点

cJSON *arr = cJSON_CreateArray(); // 创建一个数组类型的节点

cJSON_AddItemToArray(arr, cJSON_CreateString("JavaScript")); // 向数组中添加一个字符串节点

cJSON_AddItemToArray(arr, cJSON_CreateString("Java")); // 向数组中添加一个字符串节点

cJSON_AddItemToArray(arr, cJSON_CreateString("Python")); // 向数组中添加一个字符串节点

cJSON_AddItemToObject(root, "skills", arr); // 将数组节点添加到root对象中,键名为"skills"

// 输出内容

char *p = cJSON_PrintUnformatted(root); // 将JSON对象转换为字符串表示形式

printf("root = %s\n", p);

free(p);

cJSON_Delete(root); // 释放JSON对象占用的内存

return 0;

}

反序列化答案

第一种思路

#include

#include "cJSON.h"

#include

#include

#include

#include

#include

#include "list.h"

#include

#define MAX 1024

union val_t

{

int i_val;

float f_val;

char *s_val[MAX];

};

struct data {

int key;

int type;

union val_t val;

struct list_head list;

};

int main(int argc, char const *argv[])

{

// 构造链表

struct list_head head;

INIT_LIST_HEAD(&head);

struct data mydate[10]; // 创建结构体数组

// 读取文件内容到缓冲区

int fd = open("data.json", O_RDONLY);

if (fd < 0)

{

perror("open err");

return -1;

}

char buf[MAX] = {0};

size_t len = read(fd, buf, MAX);

if (len < 0)

{

perror("open err");

return -1;

}

// 反序列化动作

cJSON *root = cJSON_Parse(buf); // 把传入的字符串转成cJSON的结构(反序列化)

if (root == NULL)

{

printf("cJSON_Parse err.");

return -1;

}

// 开始摘取数据

cJSON *item;

cJSON *keyitem;

cJSON *typeitem;

cJSON *valitem;

item = cJSON_GetObjectItem(root, "ver"); // 获取root的ver子节点

printf("%s : %s\n", item->string, item->valuestring);

cJSON *login = cJSON_GetObjectItem(root, "login");

cJSON *user = cJSON_GetObjectItem(login, "user");

cJSON *pwd = cJSON_GetObjectItem(login, "pwd");

printf("%s :\n", login->string);

printf("%s : %s\n", user->string, user->valuestring);

printf("%s : %d\n", pwd->string, pwd->valueint);

// 解析数组节点

cJSON *data;

data = cJSON_GetObjectItem(root, "data");

printf("%s :\n", data->string);

int count = cJSON_GetArraySize(data); // 获取数组大小用来进行遍历

for (size_t i = 0; i < count; i++)

{

cJSON *tmp = cJSON_GetArrayItem(data, i); // 获取数组节点中的第 i 个元素

keyitem = cJSON_GetObjectItem(tmp, "key");

typeitem = cJSON_GetObjectItem(tmp, "type");

valitem = cJSON_GetObjectItem(tmp, "val");

printf("key : %d\n", keyitem->valueint);

printf("type : %d\n", typeitem->valueint);

printf("val : %s\n", valitem->valuestring);

mydate[i].key = keyitem->valueint; // 设置key值

mydate[i].type = typeitem->valueint; // 设置类型

strcpy(mydate[i].val.s_val, valitem->valuestring); // 将值拷贝到val数组

list_add(&mydate[i].list, &head); // 将结构体添加到链表中

}

// 遍历链表并输出结果

struct list_head *pos;

struct data *newtmp;

list_for_each(pos, &head)

{

newtmp = list_entry(pos, struct data, list);

printf("key = %d, type = %d, val = %s\n", newtmp->key, newtmp->type, newtmp->val.s_val);

}

list_del(pos);

cJSON_Delete(root); // 释放JSON对象占用的内存

return 0;

}

第二种思路

#include

#include

#include

#include "cJSON.h"

#include "list.h"

//定义共用体

typedef int BOOL;

union val_t

{

BOOL b_val; //bool类型存储空间

int i_val; //整形值存储空间

float f_val; //浮点值存储空间

};

//定义结构体

struct student

{

int key;

int type;

union val_t val;

struct list_head list;//指针域

};

int main(int argc, char const *argv[])

{

struct list_head head;

INIT_LIST_HEAD(&head);

char * jsonStr = "{\"ver\":\"1.0\",\"login\":{\"user\":\"zhangsan\",\"pwd\":1234},\"data\":[{\"key\":1,\"type\":2,\"val\":\"10\"},{\"key\":2,\"type\":1,\"val\":\"0\"},{\"key\":3,\"type\":3,\"val\":\"22.5\"}]}";

cJSON * root = NULL;

cJSON * item = NULL;//cjson对象

//把传入的字符串转成cJSON的结构(反序列化)

root = cJSON_Parse(jsonStr);//取出root对象(根节点)

if (!root)

{

printf("Error before: [%s]\n",cJSON_GetErrorPtr());

return -1;

}

else

{

printf("转换成功!\n");

//获取版本号--ver

item = cJSON_GetObjectItem(root, "ver");

printf("版本号%s:%s\n",item->string,item->valuestring);

//获取帐号密码

item = cJSON_GetObjectItem(root, "login");

cJSON * myuser = cJSON_GetObjectItem(item, "user");

printf("帐号%s:%s --- ",myuser->string,myuser->valuestring);

cJSON * mypwd = cJSON_GetObjectItem(item, "pwd");

printf("密码%s:%d\n",mypwd->string,mypwd->valueint);

cJSON * myData = cJSON_GetObjectItem(root, "data");//root节点下的data节点

item = myData->child;//item是数组或对象的子节点,即data节点下的每一个小的花括号节点,或者可以用cJSON *cJSON_GetArrayItem(cJSON *array,int item)获取数组中的子节点(花括号节点)

printf("data数据列表:\n");

while(item != NULL)

{

//取出data里的每一个花括号里的每一个小元素的值:即(key,type,val)的值

cJSON * mykey = cJSON_GetObjectItem(item, "key");

printf("键:%d ",mykey->valueint);

cJSON * mytype = cJSON_GetObjectItem(item, "type");

printf("类型:%d ",mytype->valueint);

cJSON * myval = cJSON_GetObjectItem(item, "val");//注意val的数据类型

printf("值:%s\n",myval->valuestring);

if(mykey != NULL && mytype != NULL && myval != NULL)

{

// printf("取值成功!以链表方式存储\n");

//将解析的数据放入链表中

struct student * stu = (struct student *)malloc(sizeof(struct student));//记得释放内存

stu->key = mykey->valueint;

stu->type = mytype->valueint;

if(mytype->valueint == 1)

{

int valnum = atoi(myval->valuestring);

stu->val.i_val = valnum;

}

else if(mytype->valueint == 2)

{

int valnum = atoi(myval->valuestring);

stu->val.i_val = valnum;

}

else if(mytype->valueint == 3)

{

int valnum = atof(myval->valuestring);

stu->val.f_val = valnum;

}

// stu->val.i_val = myval->valuestring;

//头插法插入

list_add(&stu->list,&head);

}

item = item->next;

}

//遍历打印

struct list_head *pos;//是指向一个结构体的一个指针域的指针

struct student *tmp;//是指向整个结构体的指针

// printf("init list\n");

printf("链表数据:\n");

list_for_each(pos, &head)

{

// sleep(3);

tmp = list_entry(pos, struct student, list);//可以从指针域偏移到整个结构体,拿到整个结构体的首地址,因此能够拿到属于该指针域的数据域

if(tmp->type == 1)

{

printf("key=%d, type=%d, val=%d\n", tmp->key, tmp->type, tmp->val.i_val);

}

else if(tmp->type == 2)

{

printf("key=%d, type=%d, val=%d\n", tmp->key, tmp->type, tmp->val.b_val);

}

else if(tmp->type == 3)

{

printf("key=%d, type=%d, val=%.2f\n", tmp->key, tmp->type, tmp->val.f_val);

}

}

printf("\n");

}

cJSON_Delete(root);

return 0;

}

更多尼泊尔内容

app开发不可用一般是那些原因造成的?
注册送365元可提款

app开发不可用一般是那些原因造成的?

🗓️ 07-17 👁️ 5350
GTA:OL载具篇:卡林-骷髅马(装甲版)
3658188

GTA:OL载具篇:卡林-骷髅马(装甲版)

🗓️ 07-19 👁️ 6300
便捷美食——临海小家居订餐电话及特色推荐
casino365sport365

便捷美食——临海小家居订餐电话及特色推荐

🗓️ 07-26 👁️ 1732
2025年万圣节是哪一天
注册送365元可提款

2025年万圣节是哪一天

🗓️ 08-05 👁️ 8581
美国理财攻略2024:4个高效理财方法全解析 (信用卡返现/避险/投资/省税)
鹳鹊的意思
casino365sport365

鹳鹊的意思

🗓️ 07-31 👁️ 6992
字说字话
casino365sport365

字说字话

🗓️ 08-06 👁️ 7440
司法拍卖流拍是什么意思
3658188

司法拍卖流拍是什么意思

🗓️ 07-18 👁️ 4595
网约车三证办下多少钱,网约车保险一年要13000
casino365sport365

网约车三证办下多少钱,网约车保险一年要13000

🗓️ 07-02 👁️ 4224