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

注意·:

  1. 永远用库解析——拒绝 substring/split 硬编码
  2. 防御式编程——optXXX() + 默认值 + 异常日志
  3. 验证输入——关键字段存在性/格式校验(如 Token 长度)
  4. 工具辅助——开发时用 JSONLint 校验格式

总结

“JSON 是数据的普通话——简单、通用、无歧义”

关联网络

依赖

  • XML —[依赖]→ UTF-8

对比 / 对立

  • JSON —[对比]→ XML

演化日志

  • v0.1 (2026-02-27):初始版本