JSON
起因
对 JSON 格式没有清晰认识,使用时总是遗忘,成文便于查阅。
洞见
主要梳理了 JSON 的概念、JSON 的必要性、使用 JSON 的示例,以及 JSON 的局限性。
详情
JSON 是什么?
JSON(JavaScript Object Notation)是一种轻量级、文本格式的数据交换标准。
- 本质:基于 JavaScript 对象字面量的子集,但完全独立于语言(RFC 8259 标准)。
- 核心特征:
- 人类可读(结构清晰,缩进友好)
- 机器易解析(几乎所有语言有成熟库)
- 仅含数据,无逻辑/注释(纯数据载体)
- 典型结构:
{ "user": "张三", "roles": ["admin", "editor"], "active": true, "profile": { "age": 30, "city": "北京" } }
没有 JSON 之前?
- 主流方案:XML(可扩展标记语言)
<user> <name>张三</name> <age>30</age> </user> - 痛点:
- 标签冗余(体积比 JSON 大 30%~50%)
- 解析复杂(需 DOM/SAX,代码量大)
- 与 JavaScript 交互需额外转换
- 其他方案:自定义分隔符文本(如
name|age)、二进制协议(可读性差) - 历史节点:2001 年 Douglas Crockford 正式提出 JSON,2006 年成为 IETF 标准(RFC 4627)
对比表
| 方案 | 体积 | 解析难度 | JS 交互 | 可读性 | 适用场景 |
|---|---|---|---|---|---|
| XML | 大(+30%~50%) | 高(DOM/SAX) | 需转换 | 人类可读 | 遗留系统、需 Schema 验证 |
| JSON | 小 | 极低(原生) | 无缝 | 人类可读 | 现代 Web 首选 |
| 分隔符文本 | 极小 | 中(需 split) | 简单 | 差(无结构) | 简单日志、内部管道 |
| 二进制协议 | 最小 | 中(需库) | 需转换 | ❌ 不可读 | 高性能/带宽敏感场景 |
为什么使用 JSON?
| 场景 | 优势说明 |
|---|---|
| Web 前后端 | 原生支持 JavaScript(JSON.parse()/JSON.stringify()),零转换成本 |
| API 通信 | RESTful 接口事实标准(对比 XML:体积小、解析快、移动端友好) |
| 配置文件 | 比 Properties/YAML 更结构化(如 package.json) |
| 跨语言 | Java/Python/Go/C# 等均有高性能库(Jackson/Gson/fastjson) |
| 存储 | NoSQL 数据库(MongoDB)直接存储 JSON 文档 |
核心概念
1. 基础语法规则
- 键(Key):必须用双引号包裹的字符串(
"name"✅,name❌) - 值(Value):支持 6 种类型:
字符串("text")、数字(123,3.14)、布尔(true/false)、null、对象({})、数组([]) - 禁忌:
- 末尾逗号(
{"a":1,}❌) - 单引号(
{'a':1}❌) - 注释(标准 JSON 不支持)
- 末尾逗号(
2. 数据结构
| 结构 | 符号 | 说明 | 示例 |
|---|---|---|---|
| 对象 | {} | 无序键值对(类似 Map) | {"id":1, "name":"李四"} |
| 数组 | [] | 有序值集合(类似 List) | [1, "text", {"key":"val"}] |
使用步骤(Java 实战流程)
// 1.引入库(任选其一)
// Maven: org.json / com.google.code.gson / com.fasterxml.jackson.core
// 2. 解析 JSON 字符串 → Java 对象
String json = "{\"access_token\":\"544115\",\"expires_in\":86400}";
JSONObject obj = new JSONObject(json); // org.json
// 或:Gson gson = new Gson(); Token token = gson.fromJson(json, Token.class);
// 3.安全取值(关键!)
String token = obj.optString("access_token", ""); // 避免空指针
int expires = obj.optInt("expires_in", 0); // 提供默认值
// 4.生成 JSON(反向操作)
JSONObject newJson = new JSONObject();
newJson.put("token", token);
newJson.put("timestamp", System.currentTimeMillis());
String output = newJson.toString(2); // 格式化缩进(2空格)经典示例
场景:安全提取 Token(含异常处理)
public String safeGetToken(String json) {
if (json == null || json.trim().isEmpty()) {
throw new IllegalArgumentException("JSON 为空");
}
try {
JSONObject jo = new JSONObject(json);
if (!jo.has("access_token")) {
throw new JSONException("缺少 access_token 字段");
}
return jo.getString("access_token").trim();
} catch (JSONException e) {
// 记录原始 JSON 便于排查(脱敏后)
log.error("JSON 解析失败: {}", json);
throw new RuntimeException("Token 解析异常", e);
}
}对比错误做法(避坑!)
// 依赖固定位置(JSON 顺序可能变)
String token = json.split("\"")[3];
// 无异常处理(空 JSON/字段缺失直接崩溃)
String token = new JSONObject(json).getString("access_token");局限性
| 问题 | 说明 | 应对方案 |
|---|---|---|
| 无注释 | 配置文件无法写说明(如 // 测试环境) | 用 _comment 伪字段(非标准)或选 YAML |
| 类型有限 | 无 Date/BigDecimal 等(日期需转字符串 "2024-01-01") | 自定义序列化器(Jackson @JsonFormat) |
| 大文件内存压力 | 1GB JSON 全量加载易 OOM | 流式解析(Jackson Streaming API) |
| 安全风险 | eval(json) 可能执行恶意代码 | 永远用 JSON.parse() 或安全库解析 |
| 无 Schema 验证 | 字段缺失/类型错误运行时才发现 | 配合 JSON Schema + 验证库(如 everit) |
| 中文乱码 | 未指定 UTF-8 编码时传输可能乱码 | 传输时声明 Content-Type: application/json; charset=utf-8 |
注意·:
- 永远用库解析——拒绝
substring/split硬编码- 防御式编程——
optXXX()+ 默认值 + 异常日志- 验证输入——关键字段存在性/格式校验(如 Token 长度)
- 工具辅助——开发时用 JSONLint 校验格式
总结
“JSON 是数据的普通话——简单、通用、无歧义”
关联网络
依赖
- XML —[依赖]→ UTF-8
对比 / 对立
- JSON —[对比]→ XML
演化日志
- v0.1 (2026-02-27):初始版本