JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,自2001年由Douglas Crockford提出以来,已成为互联网数据交换的事实标准。它基于JavaScript的对象字面量语法,但独立于编程语言,几乎所有主流语言都提供了JSON的解析和生成支持。掌握JSON不仅是开发基础,更是高效协作的前提——无论是API交互、配置管理还是数据存储,JSON都在其中扮演着关键角色。
一、JSON的基础概念与历史
1.1 定义与定位
JSON是一种文本格式的结构化数据表示法,旨在实现跨平台、跨语言的数据交换。它的设计目标是替代繁琐的XML,提供更简洁、易读、高效的数据传输方式。与编程语言绑定的“对象”不同,JSON是纯数据格式,不包含代码逻辑,仅用于描述数据的结构和值。
1.2 历史背景
JSON的起源与JavaScript密切相关。20世纪90年代末,JavaScript作为浏览器脚本语言普及后,开发者需要一种简单的格式在客户端与服务器之间交换数据。当时XML是主流,但存在语法冗余(如闭合标签、命名空间)、解析复杂等问题。
2001年,Douglas Crockford在JavaScript对象字面量的基础上简化语法,提出了JSON格式。2006年,JSON被纳入ECMA标准(ECMA-404),2013年成为IETF标准(RFC 7159),正式确立了其行业地位。如今,JSON已广泛应用于API交互、配置文件、数据存储等场景。
二、JSON的语法规则与数据类型
JSON的语法基于“键值对”和“有序集合”,整体结构简洁且严格。不符合语法规则的JSON会被解析器视为无效,因此掌握语法细节至关重要。
2.1 基本结构
JSON数据的根结构只能是对象(object) 或数组(array),这是与JavaScript对象的核心区别(JavaScript对象可单独存在,无需包裹)。
对象:用
{}
包裹,由若干键值对组成,键值对之间用逗号分隔(最后一个键值对后不能有逗号)。
示例:{"name": "Alice", "age": 30}
数组:用
[]
包裹,由若干值组成,值之间用逗号分隔(最后一个值后不能有逗号)。
示例:["apple", "banana", "cherry"]
2.2 数据类型详解
JSON支持6种基本数据类型,每种类型有严格的格式约束:
(1)字符串(string)
- 必须用双引号包裹(单引号无效)。
- 支持转义字符,常见转义序列如下:
\"
(双引号)、\\
(反斜杠)、\/
(斜杠)、\b
(退格)、\f
(换页)、\n
(换行)、\r
(回车)、\t
(制表符)、\uXXXX
(Unicode字符,如\u4e2d
表示“中”)。 - 示例:
{"message": "Hello \"World\"!\nWelcome"}
(2)数字(number)
- 支持整数、浮点数、负数,不支持八进制(如
012
)、十六进制(如0x1a
)。 - 格式约束:
- 整数:
123
、-456
(不能以 leading zero 开头,如012
无效,但0
有效)。 - 浮点数:
3.14
、-0.5
、1e3
(科学计数法,等价于1000)、2E-4
(等价于0.0002)。
- 整数:
- 示例:
{"pi": 3.14159, "large": 1e+20, "small": -2.5e-3}
(3)布尔值(boolean)
- 仅支持
true
和false
(小写,True
或FALSE
均无效)。 - 示例:
{"isActive": true, "hasPermission": false}
(4)空值(null)
- 仅支持
null
(小写,Null
或NULL
无效),表示“无值”。 - 示例:
{"middleName": null}
(5)对象(object)
- 键(key)必须是字符串(双引号包裹),不能是数字或表达式。
- 键值对的“值”可以是任意JSON数据类型(包括嵌套对象或数组)。
- 示例:
{ "user": { "id": 1001, "profile": { "name": "Bob", "hobbies": ["reading", "hiking"] } } }
(6)数组(array)
- 数组元素可以是任意JSON数据类型,且类型可混合。
- 支持多维数组(数组嵌套数组)。
- 示例:
{ "matrix": [ [1, 2, 3], [4, "five", 6], [7, null, true] ] }
2.3 语法禁忌
- 不支持注释:原生JSON语法中不允许注释(
//
或/* */
均无效),这是为了保持格式简洁,但实际使用中可通过JSON5等扩展格式解决。 - 尾部逗号:对象或数组的最后一个元素后不能有逗号(如
{"a": 1,}
或[1, 2,]
均无效)。 - 键的格式:键必须用双引号包裹,且不能重复(解析时可能覆盖前值,具体取决于解析器)。
- 特殊值:不支持
undefined
、函数、日期对象等(需手动转换为字符串或数字)。
三、JSON与其他数据格式的对比
JSON的流行离不开与其他格式的对比优势,尤其是与XML的竞争。以下从多个维度对比主流数据格式:
3.1 JSON vs XML
XML是JSON的主要替代者,二者均用于数据交换,但设计理念差异显著:
维度 | JSON | XML |
---|---|---|
语法简洁性 | 无冗余标签,用{} /[] 表示结构,更紧凑 |
依赖闭合标签(如<tag></tag> ),冗余度高 |
可读性 | 结构清晰,适合人类快速理解 | 标签嵌套复杂时可读性下降 |
解析效率 | 解析速度快(无标签解析开销) | 解析需处理标签层次,效率较低 |
数据类型 | 原生支持6种类型(字符串、数字等) | 所有数据均为字符串,需手动定义类型 |
扩展性 | 弱(无命名空间、注释等) | 强(支持命名空间、注释、DTD等) |
适用场景 | API交互、轻量数据交换 | 复杂文档标记(如HTML)、企业级数据交换 |
示例:描述“用户信息”的两种格式
JSON:
{"user": {"name": "Alice", "age": 30, "hobbies": ["music", "sports"]}}
XML:
<user>
<name>Alice</name>
<age>30</age>
<hobbies>
<hobby>music</hobby>
<hobby>sports</hobby>
</hobbies>
</user>
可见,JSON在数据交换场景中更简洁高效,而XML在需要复杂文档结构的场景中更有优势。
3.2 JSON vs YAML
YAML是另一种轻量格式,强调“人类友好”,支持注释和更灵活的语法:
- YAML用缩进表示层次(无
{}
/[]
),支持注释(#
),但语法对缩进敏感(易出错)。 - JSON是YAML的子集(大部分JSON可直接作为YAML解析),但YAML支持更多类型(如日期、锚点引用)。
- 适用场景:YAML更适合配置文件(如Docker Compose),JSON更适合API交互。
3.3 JSON vs CSV
CSV(逗号分隔值)适用于表格数据:
- 优势:纯文本、体积小,适合批量数据(如Excel导出)。
- 劣势:不支持嵌套结构,无法表示复杂关系(如数组、对象)。
- 适用场景:简单表格数据交换,无法替代JSON的复杂结构支持。
四、JSON的解析与生成(跨语言实现)
JSON的跨语言支持是其普及的核心原因。几乎所有主流语言都提供了内置或第三方库用于JSON的“序列化”(对象→JSON字符串)和“反序列化”(JSON字符串→对象)。
4.1 JavaScript中的JSON处理
JavaScript原生提供JSON
对象,包含两个核心方法:
(1)JSON.stringify():序列化
将JavaScript对象转换为JSON字符串。
语法:JSON.stringify(value[, replacer[, space]])
value
:待序列化的对象。replacer
:可选函数或数组,用于过滤/转换值(如排除敏感字段)。space
:可选数字或字符串,用于格式化输出(美化)。
示例:
const user = { name: "Bob", age: 25, isAdmin: false };
// 基础序列化
const jsonStr = JSON.stringify(user);
// 结果:'{"name":"Bob","age":25,"isAdmin":false}'
// 过滤字段(只保留name)
const filtered = JSON.stringify(user, ["name"]);
// 结果:'{"name":"Bob"}'
// 美化输出(缩进2空格)
const pretty = JSON.stringify(user, null, 2);
// 结果:
// '{\n "name": "Bob",\n "age": 25,\n "isAdmin": false\n}'
注意:JSON.stringify()
会忽略undefined
、函数和Symbol,循环引用会抛出错误。
(2)JSON.parse():反序列化
将JSON字符串转换为JavaScript对象。
语法:JSON.parse(text[, reviver])
text
:JSON字符串。reviver
:可选函数,用于转换解析后的值(如恢复日期对象)。
示例:
const jsonStr = '{"name":"Alice","birth":"2000-01-01"}';
// 基础解析
const user = JSON.parse(jsonStr);
// 结果:{ name: "Alice", birth: "2000-01-01" }(birth是字符串)
// 恢复日期对象
const userWithDate = JSON.parse(jsonStr, (key, value) => {
if (key === "birth") return new Date(value);
return value;
});
// 结果:{ name: "Alice", birth: Date对象 }
解析错误:若字符串不符合JSON语法,会抛出SyntaxError
(如键未用双引号、存在注释等)。
4.2 其他语言的JSON库
(1)Python
Python内置json
模块,用法与JavaScript类似:
import json
# 序列化
user = {"name": "Charlie", "age": 30}
json_str = json.dumps(user, indent=2) # indent用于美化
# 反序列化
parsed = json.loads(json_str)
(2)C++
以下是三个主流C++ 的JSON库:
1. nlohmann/json
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main() {
json j = {{"name", "John"}, {"age", 30}};
std::cout << j.dump() << std::endl;
}
2. RapidJSON
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
int main() {
rapidjson::Document d;
d.Parse(R"({"name":"Alice"})");
printf("Name: %s\n", d["name"].GetString());
}
3. Boost.JSON
#include <boost/json/src.hpp>
namespace json = boost::json;
int main() {
json::value j = json::parse(R"({"age":25})");
std::cout << j.at("age").as_int64() << std::endl;
}
使用前需安装对应库,选择依据:
- 简单易用 → nlohmann/json
- 高性能 → RapidJSON
- Boost集成 → Boost.JSON
4.3 常见问题与解决方案
- 日期处理:JSON不支持日期类型,需手动转换(如序列化为ISO字符串
"2023-10-01T12:00:00Z"
,反序列化时再转为日期对象)。 - 循环引用:对象中存在循环引用(如
a.b = a
)时,JSON.stringify()
会报错,需通过replacer
排除引用。 - 大数据量:超大JSON(如1GB+)需使用流式解析(如Python的
ijson
、Java的Jackson Streaming API),避免内存溢出。
五、JSON的衍生格式与扩展
原生JSON的局限性(如无注释、严格语法)催生了多种衍生格式,以适应不同场景需求。
5.1 JSON5:解决原生JSON的痛点
JSON5是JSON的超集,放宽了语法限制,更贴近JavaScript对象字面量:
- 支持注释(
//
单行注释、/* */
多行注释)。 - 键可省略双引号(若符合标识符规则)。
- 支持单引号字符串。
- 允许尾部逗号。
- 支持十六进制(
0x123
)和八进制(0o755
)。
示例:
{
// 用户信息
name: 'Frank', // 单引号字符串
age: 33,
hobbies: [
'coding',
'gaming', // 尾部逗号
],
hex: 0x1a,
oct: 0o777
}
适用场景:配置文件(如tsconfig.json5
)、开发环境中的临时数据,需注意:JSON5需专用解析器(如json5
npm包),不可直接用原生JSON解析器。
5.2 GeoJSON:地理空间数据格式
GeoJSON是JSON的子集,用于描述地理空间信息(点、线、面等),被GIS(地理信息系统)广泛采用。
核心类型:Point
(点)、LineString
(线)、Polygon
(面)等,示例:
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [116.404, 39.915] // 经纬度:北京
},
"properties": { "name": "天安门" }
}
5.3 JSON-LD:链接数据格式
JSON-LD(JSON for Linking Data)通过@context
定义语义,使JSON数据具备“链接性”,适用于语义网(如知识图谱)。
示例:
{
"@context": "http://schema.org",
"@type": "Person",
"name": "Grace",
"url": "https://example.com/grace"
}
5.4 BSON与MessagePack:二进制JSON
文本JSON在传输/存储时体积较大,二进制格式应运而生:
- BSON(Binary JSON):MongoDB的存储格式,支持日期、二进制数据、正则表达式等类型,比JSON更紧凑。
- MessagePack:更轻量的二进制格式,兼容JSON类型,体积比JSON小30%-50%,适用于高性能场景(如游戏服务器)。
六、JSON的应用场景
JSON的灵活性使其在多个领域成为首选格式,以下是典型应用场景:
6.1 API数据交换
RESTful API几乎全采用JSON作为数据格式,替代了早期的XML。例如:
- GitHub API:
GET /users/octocat
返回用户信息的JSON响应。 - 支付接口:回调通知以JSON格式传递订单状态。
优势:解析高效,跨语言兼容性好,便于前后端分离开发。
6.2 配置文件
JSON(或JSON5)常用于配置文件,比XML更简洁,比INI更支持复杂结构:
package.json
:npm项目的核心配置(依赖、脚本等)。tsconfig.json
:TypeScript编译配置。- 工具配置(如ESLint、Prettier的配置文件)。
6.3 数据存储
NoSQL数据库(如MongoDB、CouchDB)采用JSON-like格式存储数据:
- MongoDB的文档是BSON格式(兼容JSON),支持嵌套结构,适合存储非结构化数据。
- 示例MongoDB文档:
{ "_id": "ObjectId(\"60d21b4667d0d8992e610c85\")", "username": "hana", "posts": [ { "title": "JSON教程", "date": "2023-10-01" } ] }
6.4 日志格式
结构化日志(如ELK栈)常用JSON格式,便于检索和分析:
{
"timestamp": "2023-10-01T12:00:00Z",
"level": "ERROR",
"message": "数据库连接失败",
"details": { "code": 500, "host": "db.example.com" }
}
七、JSON的安全性与最佳实践
尽管JSON本身设计简单,但在使用中仍需注意安全风险和规范问题。
7.1 安全性风险
(1)JSON注入
当服务器通过字符串拼接生成JSON(而非正规序列化)时,可能导致注入攻击。
示例:用户输入name": "hacker", "isAdmin": true, "x": "
,若直接拼接:
// 危险做法:字符串拼接
const json = `{"name": "${userInput}"}`;
// 结果:{"name": "hacker", "isAdmin": true, "x": ""}(注入了isAdmin字段)
解决方案:使用正规序列化方法(如JSON.stringify()
),避免手动拼接。
(2)JSON劫持
早期漏洞:利用<script>
标签加载JSON数据(如http://api.com/user
),结合数组构造函数覆盖窃取数据。
防护:现代浏览器已禁止重写数组/对象的构造函数,且API应使用Content-Type: application/json
(避免被<script>
解析)。
(3)大数据量攻击
恶意发送超大JSON(如1GB)可能导致服务器解析时内存溢出,需限制JSON大小(如API设置maxBodyLength
)。
7.2 JSON Schema:数据验证
JSON Schema用于定义JSON数据的结构约束(如必填字段、类型、格式),确保数据一致性(尤其适用于API开发)。
示例:用户信息的Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["name", "email"], // 必填字段
"properties": {
"name": { "type": "string", "minLength": 1 }, // 字符串且非空
"age": { "type": "number", "minimum": 0 }, // 数字且≥0
"email": { "type": "string", "format": "email" } // 邮箱格式
}
}
常用验证工具:Ajv(JavaScript)、jsonschema(Python)。
7.3 最佳实践
- 使用正规库解析:避免手动解析JSON(易出错),优先使用语言内置/成熟库。
- 格式化输出:生产环境用压缩JSON(减小体积),开发环境用美化格式(便于调试)。
- 处理日期:统一用ISO 8601格式(
YYYY-MM-DDTHH:mm:ssZ
)存储日期。 - 避免深层嵌套:嵌套过深(如10层以上)会降低可读性和解析效率,建议扁平化处理。
- 版本兼容:API升级时,新增字段应兼容旧版本(避免删除/修改已有字段)。
八、JSON的未来趋势
JSON作为主流数据格式,短期内地位难以撼动,但也在持续演进:
- 标准化扩展:JSON5可能被纳入标准,原生支持注释等功能。
- 二进制格式普及:MessagePack、BSON等在高性能场景(如物联网、实时通信)的应用将增加。
- 与AI结合:JSON作为数据交换格式,将更深度融入大模型API(如OpenAI API的JSON响应)。