【网络编程】网络传输-JSON
一、JSON概述
JSON(JavaScript Object Notation)是一种轻量级、基于文本的数据交换格式,旨在简洁地表示结构化数据,同时兼顾人类可读性和机器解析效率。它起源于 JavaScript 的对象语法,但目前已成为独立于编程语言的通用数据格式,被广泛用于前后端通信、服务间数据交换、配置文件等场景。
JSON格式主要有以下的特点:
- 相比 XML 等格式,JSON 语法简洁(无冗余标签),数据体积更小,传输和解析效率更高。
- 基于 Unicode 字符,可直接被人类阅读和编写,便于调试和手动修改。
- 几乎所有编程语言(C++、Java、Python、JavaScript 等)都有成熟的 JSON 解析 / 生成库,无需额外适配
- 数据结构本身包含字段名,无需额外 schema 即可理解数据含义
二、JSON的语法规则
JSON
的语法比较简单,主要是由一个个键值对和有序列表组成,语法规则如下:
2.1 基本结构
-
对象(Object):表示键值对的集合,用
{}
包裹,键值对之间用逗号,
分隔。- 键(key)必须是字符串,且用双引号
"
包裹(不能用单引号)。 - 值(value)可以是字符串、数字、布尔值、null、对象或数组。
- 键(key)必须是字符串,且用双引号
-
数组(Array):表示有序的值列表,用
[]
包裹,元素之间用逗号,
分隔,元素类型可以是任意 JSON 数据类型(包括对象、数组)。
2.2 数据类型
- 字符串:必须用双引号
"
包裹,支持转义字符(如\"
表示双引号、\n
表示换行),例如"name": "Alice"
。 - 数字:整数(如
42
)、浮点数(如3.14
)、负数(如-10
),不支持八进制 / 十六进制(需用十进制表示)。 - 布尔值:
true
或false
(小写)。 - null:表示空值(小写
null
)。 - 对象(Object):嵌套的键值对集合,例如
{"user": {"id": 1, "name": "Bob"}}
。 - 数组(Array):有序元素列表,例如
{"hobbies": ["reading", "coding"]}
。
注意事项:
- 键必须是字符串(双引号包裹),不能是数字或其他类型。
- 元素之间用逗号分隔,但最后一个元素后不能有逗号(否则会解析错误)。
- 区分大小写(
true
正确,True
错误)。 - 不支持注释(标准 JSON 语法中无注释,部分扩展格式如 JSON5 支持,但非通用)
2.3 JSON
数据结构示例
2.3.1 简单JSON
结构
{"name": "张三","age": 25,"isStudent": false,"height": 1.75,"address": null
}
2.3.2 嵌套JSON
结构
{"user": {"id": 1001,"info": {"name": "李四","contact": {"phone": "123456789","email": "lisi@example.com"}}}
}
2.3.3 数组(有序列表)
{"fruits": ["apple", "banana", "orange"],"scores": [90, 85.5, 95],"isPass": [true, false, true]
}
2.3.4 复杂JSON
结构(数组+嵌套)
{"class": "高三(1)班","students": [{"name": "王五","age": 18,"subjects": ["数学", "英语"]},{"name": "赵六","age": 17,"subjects": ["物理", "化学"]}]
}
三、JSON的使用场景
-
数据交换:
- 前后端通信:浏览器与服务器之间的 HTTP 请求 / 响应(如 AJAX、RESTful API),几乎是标准格式。
- 服务间通信:微服务架构中,不同服务(如 Java 后端与 C++ 服务)通过 JSON 交换数据,避免语言差异导致的兼容性问题。
-
配置文件:
- 相比 XML 更轻量,比 INI 更灵活(支持嵌套结构),例如:
{"server": {"port": 8080,"timeout": 30,"log": {"level": "info","path": "/var/log/server.log"}}
}
- 数据存储:
部分 NoSQL 数据库(如 MongoDB)直接使用 JSON-like 格式(BSON)存储数据,便于灵活扩展字段。
四、序列化和反序列化JSON
序列化和反序列化JSON
有许多成熟的处理库,比如使用C++
编程语言可以使用nlohmann/json
库,这是一个轻量级的JSON
库,风格偏向现代C++
,并且只有一个.hpp
文件,不需要额外链接库
4.1 下载nlohmann-json
在Ubuntu
下,可以使用包管理器下载,下载完成后自动安装到系统路径/usr/include
下
sudo apt update
sudo apt install nlohmann-json3-dev
Windows
可以在github
上下载源码,手动安装:https://github.com/nlohmann/json/releases
4.2 序列化JSON
我们下面介绍一个例子,用于序列化嵌套的JSON
,可以发现,这个库的语法风格十分像STL
,对STL
熟悉的话就可以自己琢磨出用法,详细手册查看:https://json.nlohmann.me/
- 可以使用
[]
来添加对应key-value
值,这里会发送自动的类型转换,key
一般是std::string
类型 - 或者使用
at()
函数,这个是返回一个引用,因此可以修改对应的值,并且它具有边界检查 - 嵌套
JSON
就是将key
设置为JSON
类型 - 初始化
JSON
类型支持初始化列表的模式
void serializeJson(LoginMsg const& msg,json* pJson){(*pJson)["user"] = msg.user;(*pJson)["password"] = msg.pwd;//嵌套Jsonjson jDate = {{"year",msg.date.year},{"month",msg.date.month},{"day",msg.date.day}};(*pJson)["date"] = jDate;json jTime = {{"hour",msg.time.hour},{"minute",msg.time.min},{"second",msg.time.sec}};(*pJson)["time"]= jTime;std::cout << "===== serialize json =====" << std::endl; std::cout << pJson->dump(4) << std::endl;
}
序列化好的JSON
数据结构可以使用dump
成员函数来打印,其中dump(4)
表示设置4
位的缩进值
{"date": {"day": 20,"month": 7,"year": 2025},"password": "xx123456789","time": {"hour": 14,"minute": 30,"second": 15},"user": "Antonio"
}
4.3 反序列化JSON
- 反序列化
JSON
可以从json
类中像STL
的map
一样通过[]
和at()
访问,与上述序列化一致 - 不论是
[]
还是at()
,都是返回一个json
的引用,因此支持链式调用,用起来很方便
void deserializeJson(json* pJson){std::cout << "===== deserialize json =====" << std::endl;std::string user = pJson->at("user");std::string password = pJson->at("password");Date date;date.year = pJson->at("date").at("year");date.month = pJson->at("date").at("month");date.day = pJson->at("date").at("day");Time time;time.hour = pJson->at("time").at("hour");time.min = pJson->at("time").at("minute");time.sec = pJson->at("time").at("second");std::cout << "user = " << user << std::endl;std::cout << "password = " << password << std::endl;std::cout << "date = [" << " year = " << date.year << " month = " << date.month << " day = " << date.day <<" ]" << std::endl;;std::cout << "time = [" << " hour = " << time.hour << " minute = " << time.min << " second = " << time.sec << " ]" << std::endl;
}
4.4 测试结果
完整的测试代码如下
#include<iostream>
#include<nlohmann/json.hpp>using json = nlohmann::json;struct Date{int year;int month;int day;
};struct Time{int hour;int min;int sec;
};struct LoginMsg{std::string user;std::string pwd;Date date;Time time;};void serializeJson(LoginMsg const& msg,json* pJson){(*pJson)["user"] = msg.user;(*pJson)["password"] = msg.pwd;//嵌套Jsonjson jDate = {{"year",msg.date.year},{"month",msg.date.month},{"day",msg.date.day}};(*pJson)["date"] = jDate;json jTime = {{"hour",msg.time.hour},{"minute",msg.time.min},{"second",msg.time.sec}};(*pJson)["time"]= jTime;std::cout << "===== serialize json =====" << std::endl; std::cout << pJson->dump(4) << std::endl;
}void deserializeJson(json* pJson){std::cout << "===== deserialize json =====" << std::endl;std::string user = pJson->at("user");std::string password = pJson->at("password");Date date;date.year = pJson->at("date").at("year");date.month = pJson->at("date").at("month");date.day = pJson->at("date").at("day");Time time;time.hour = pJson->at("time").at("hour");time.min = pJson->at("time").at("minute");time.sec = pJson->at("time").at("second");std::cout << "user = " << user << std::endl;std::cout << "password = " << password << std::endl;std::cout << "date = [" << " year = " << date.year << " month = " << date.month << " day = " << date.day <<" ]" << std::endl;;std::cout << "time = [" << " hour = " << time.hour << " minute = " << time.min << " second = " << time.sec << " ]" << std::endl;
}int main(){std::string user = "Antonio";std::string password = "xx123456789";Date date = {2025,7,20};Time time = {14,30,15};LoginMsg loginMsg = {user,password,date,time};json* pJLoginMsg = new json();serializeJson(loginMsg,pJLoginMsg);deserializeJson(pJLoginMsg);delete pJLoginMsg;return 0;
}
序列化JSON
和反序列化JSON
的测试输出结果如下:
更多资料:https://github.com/0voice