📘 第十章 构建Web内容的技术
📄 一句话概括

JSON 是从 JavaScript 的对象字面量里抠出来的一个子集,只描述数据结构,不描述类型和语义。 同一批数据比 XML 少 48%,代价是没有注释、没有属性、没有命名空间。

📝 本节和前面几节的关系

上一节用 XML 传数据,看得出一件事:标签太占地方。这一节看那个把标签全省掉的格式。

一、它从哪来

JavaScript 里写一个对象本来就长这样:{ "a": 1, "b": [2, 3] }。 道格拉斯·克罗克福德看出这段语法本身就能当数据格式用,于是把它单独拎出来,去掉函数、 去掉 undefined、强制键必须加双引号,剩下的就是 JSON。 所以 JSON 不是 JavaScript——它是 JS 字面量的一个子集, 恰好能被 JS 直接 eval(后来改成 JSON.parse,安全得多)。 它的正式规范是 RFC 8259(2017),之前的 RFC 4627、RFC 7159 是前身。 这个出身很关键:它一开始就不是为了「描述文档」,而是为了「描述一个数据结构」。 所以它没有属性、没有命名空间、没有 DTD,这些在 XML 里都有。

二、六种值,就这些

对象 {}、数组 []、字符串 "abc"、数字 12 或 1.5、布尔 true/false、空 null。 就这六种,没有第七种。 Demo 把三条数据分别写成三种形态:RSS 那种 XML 675 字节,JSON 美化版 411 字节, 紧凑版 350 字节——紧凑版比 XML 少 325 字节,省 48%。 少在哪?少在标签。XML 每条要写两遍 title(开一次合一次),JSON 只写一次键名。 三条数据乘下来,一半的字节就省掉了。 还有一个反直觉的点:如果强制把中文转成 \uXXXX(ensure_ascii=True),字节数从 350 涨到 440, 多花 26%。因为一个汉字在 UTF-8 里是 3 字节,转义之后变成 6 字节。 所以发给浏览器时别开这个选项。

三、它不能做什么

不能写注释。规范明确禁止,所以配置文件里想加一句说明只能塞个假字段。 没有属性。XML 里 <book id="b1"> 那种「元数据挂在元素上」的写法,JSON 只能改成多一个键。 没有类型系统。"2026-10-20" 在 JSON 里就是个字符串,它不会知道这是日期。 要区分类型,只能靠字段名或者额外约定。 数字就是 IEEE 754 双精度。 Demo 里 0.1 + 0.2 打出来是 0.30000000000000004——这不是 JSON 的错,是所有浮点的通病, 但用 JSON 传金额时要特别小心,最好传字符串或整数分。

四、动手 Demo:同一批数据,第三种写法

把前面那三条数据写成 JSON 的三种形态(美化、紧凑、强制转义),量字节;和 RSS 那份 XML 对比; 再解析回来验证没有丢信息。最后把六种值类型列成表。

J_COMPACT = json.dumps(DATA, ensure_ascii=False, separators=(',', ':'))
J_ASCII = json.dumps(DATA, ensure_ascii=True, separators=(',', ':'))

print(len(J_COMPACT.encode('utf-8')))   # 350
print(len(J_ASCII.encode('utf-8')))     # 440
print(json.loads(J_COMPACT) == DATA)    # True

实跑输出:

--------------------------------------------------------------------
JSON:同一批数据,又一种写法
--------------------------------------------------------------------
  RSS 那种 XML 写法          675 字节(3 条)
  JSON 美化版                411 字节
  JSON 紧凑版                350 字节
  JSON 强制转义非 ASCII      440 字节
  紧凑版比 XML 少            325 字节,48%
  转义非 ASCII 多花          90 字节,26%

  紧凑版的样子(前 76 个字节):
    [{"title":"图解 HTTP 读完了","url":"https://example.com/p/1","date":"2026-10-20",…

  解析回来条数               3 条
  第一条的字段               date、desc、title、url
  和原数据一致吗             一致

  类型           JSON 写法    Python 里对应
  对象           { }        dict
  数组           [ ]        list
  字符串          "abc"      str
  数字           12 / 1.5   int / float
  布尔           true / false True / False
  空            null       None

  JSON 里不能写注释          是
  JSON 里没有属性与命名空间  是
  键必须是字符串             是

  这三条正好是 XML 擅长的地方,也是 JSON 敢说自己「轻」的原因:
  它只描述数据结构,不描述「这是什么类型的数据」「这个字段该怎么处理」。
  那些事交给人和文档,协议不管。

  0.1 加 0.2 的结果          0.30000000000000004
  这一行是提醒:JSON 的数字就是 IEEE 754 双精度,
  所以金额之类的东西别拿浮点存——和 JSON 没关系,是所有浮点的通病。

XML 675 字节,JSON 美化版 411,紧凑版 350,省 48%。 差别全在标签上:XML 每个字段要开一次合一次。 强制转义非 ASCII 之后从 350 涨到 440,多花 26%。 因为一个汉字 UTF-8 是 3 字节,转成 \uXXXX 变 6 字节。 json.loads 之后和原数据完全一致——三种写法丢的信息一样多,差别只在字节。 六种值类型表里最需要注意的是「数字」那一行:JSON 只有一种数字,不区分整数和浮点, 所以 12 和 12.0 在某些语言里会被解析成不同类型。

30 秒实验

  1. 把 ensure_ascii 改成 True 再发一次请求,用开发者工具看响应体大小。
  2. 把金额改成 19.99,算十次加法,看误差怎么累积。
  3. 想一想:为什么配置文件(比如 VS Code 的 settings.json)可以接受「不能写注释」这个限制?

常见误区

  1. 「JSON 是 JavaScript 的一部分」——它是 JS 字面量的子集,但两种语言都能用。
  2. 「JSON 比 XML 高级」——各有取舍,JSON 省字节但丢了类型和命名空间。
  3. 「JSON 的数字能精确表示金额」——它就是浮点,金额别用它算。
自测三问(点开对答案) 1. JSON 一共有几种值类型? —— 六种:对象、数组、字符串、数字、布尔、null。 2. JSON 比 XML 省在哪? —— 省在标签:XML 每个字段要开合两次,JSON 只写一次键名。 3. 为什么 `ensure_ascii=True` 会让体积变大? —— 汉字被转成 `\uXXXX`,3 字节变 6 字节。

第十章讲完了。最后一章换个角度:前面讲的每一样东西,都能被怎么攻击。

相关笔记