JSON 语法与数据模型
本节目标
学完后,你能把“文本长得像 JSON”和“它是合法 JSON”分开;能识别六类值、对象成员和数组元素;能解释顶层标量、合法空白、转义,以及缺失、null 和空值为什么不是一回事。
先分清三层对象
初学时最容易把下面三样混在一起:
python_value = {"enabled": True, "fallback": None}
json_text = '{"enabled": true, "fallback": null}'
json_string_value = '"{\\"enabled\\": true}"'python_value是内存中的 Pythondict;json_text是 Python 字符串,字符串内容恰好是一份 JSON 文本;json_string_value解析后得到的仍是字符串,其内容才像一个对象。这通常是误做了两次序列化。
接口传输的是字节;解析器把字节解码为文本,再把文本转换成语言对象。后续 Schema 验证和业务判断处理的是解析后的值,不是大括号本身。
一个 JSON 文本表示一个值
RFC 8259 的核心形式可以读作:合法空白 + 一个值 + 合法空白。顶层值不必是对象或数组,以下都是合法的单个 JSON 文本:
{"name":"agent"}["search", "read"]42null某些 API 另行要求顶层必须是对象。这是接口契约,不是 JSON 通用语法。收到数据后应先解析,再显式检查顶层类型。
六类值与 Python 映射
{
"name": "meeting-assistant",
"enabled": true,
"max_steps": 8,
"temperature": 0.2,
"tools": ["search_notes", "read_calendar"],
"fallback": null,
"limits": {
"timeout_seconds": 30
}
}| JSON 类别 | 例子 | Python 默认结果 | 关键边界 |
|---|---|---|---|
| object | {"a": 1} | dict | 成员名必须是字符串;语义上不依赖顺序。 |
| array | [1, "x", null] | list | 元素有序且可以异构。 |
| string | "agent" | str | 使用双引号;控制字符需转义。 |
| number | 8、0.2、1e3 | int、float | JSON 不声明位宽、Decimal 或日期类型。 |
| boolean | true、false | True、False | JSON 使用小写字面量。 |
| null | null | None | 表示显式空值;具体业务含义由契约定义。 |
对象成员写成 "name": value,成员之间用逗号;数组元素之间也用逗号。最后一项后不能保留尾逗号。
合法空白非常有限
JSON 语法只把四种字符当作结构外空白:空格、Tab、LF 和 CR。全角空格、不可见的任意 Unicode 空白或复制来的特殊字符不一定合法。为了调试,先查看解析错误的行列号和原始字节来源,不要通过“删除所有看不见字符”掩盖数据问题。
最常见的非法写法
下面每一行都不是标准 JSON:
{'name': 'agent'} # 单引号
{name: "agent"} # 键没有双引号
{"enabled": True} # Python 布尔值
{"fallback": None} # Python 空值
{"items": [1, 2,]} # 尾逗号
{"value": undefined} # JSON 没有 undefined
{"id": 001} # 整数有无意义前导零
{"score": NaN} # RFC 8259 不允许非有限数字
{"hex": 0x10} # 不支持十六进制数字
{"a": 1} {"b": 2} # 两个值没有 framing示例后的 # 是教学说明,不属于被判断的文本。标准 JSON 本身不支持注释。JSON5 等扩展格式可以允许其中一些写法,但不能把扩展语法悄悄送给严格 JSON API。
字符串、转义与 Windows 路径
字符串中的双引号、反斜杠和 U+0000–U+001F 控制字符必须转义:
{
"quote": "他说:\"停止任务\"",
"path": "D:\\data\\input.json",
"two_lines": "first\nsecond",
"tab": "left\tright",
"chinese": "中文可直接使用 UTF-8"
}不要把 JSON 转义和 Python 源码转义混为一层。在 Python 源码里手写同一文本时,反斜杠可能还要再经过 Python 字符串解释。更可靠的方式是先构造 Python 对象,再让 json.dumps 编码。
缺失、null、空字符串与空容器
{}{"deadline": null}{"deadline": ""}{"reviewers": []}它们分别可能表示:
- 字段缺失:未提供、沿用旧值或使用默认值;
null:显式清除或未知;- 空字符串:提供了字符串,但没有字符;
- 空数组:提供了一个集合,而且集合当前没有元素。
解析器只保留数据形状,不知道“清除”和“沿用”哪一个才是业务意图。契约必须为每个字段写清楚。
文本相同、值相同与业务相同
下面两段文本字节不同,但解析后的对象可以表示相同的名值映射:
{"a":1,"b":2}{
"b": 2,
"a": 1
}对象成员顺序不应成为跨系统业务语义;数组顺序则有语义。字符串 "1" 与数字 1 不相等,null 与字段缺失也不相等。若要做签名、哈希或缓存键,不能直接假定任意序列化文本稳定,后续会学习 canonicalization。
常见错误与排查
- 把 Python
repr复制为 JSON:先用json.dumps生成,再用解析器验证。 - 手工拼接 JSON:引号、反斜杠和控制字符容易破坏边界;构造对象后序列化。
- 看到
JSONDecodeError就修改 Schema:语法解析发生在 Schema 之前,先修文本。 - 假定所有 JSON 都是对象:顶层可以是标量,但应用可以显式拒绝。
- 把注释写进生产 payload:改用独立文档字段或选择明确支持注释的配置格式。
练习
- 把 Python 字面量
{'ok': True, 'value': None}改成合法 JSON。 - 为路径
D:\agent\config.json和包含双引号的句子写合法 JSON 字符串。 - 判断
42、[true]、{"x": 1,}、{"x": +1}是否合法,并说明理由。 - 为 PATCH 请求中的
display_name定义“缺失、null、空字符串”三种语义。 - 解释为什么对象字段换序通常不应导致业务变化,而数组元素换序可能会。
自测
- JSON 对象键能否是未加引号的数字?
- 顶层
false是否是合法 JSON 文本? - JSON 是否原生支持日期、注释和
undefined? - 为什么 JSON 字符串中的 Windows 路径要写双反斜杠?
- 文本不同是否必然表示解析后的值不同?
小结与下一步
JSON 是“一个序列化值”的语法,不是权限系统、数据库或业务模型。下一节把文本交给 Python,并观察标准库为兼容性保留的默认行为:Python 解析、序列化与严格模式。返回 JSON 学习目录。
参考资料
资料复核日期:2026-07-14。