JSON 入门:语法、数据类型和错误修复
2026 年 11 月更新
JSON(JavaScript Object Notation)是一种表示结构化数据的纯文本格式。它最初只是 JavaScript 对象语法的一个子集,如今几乎所有编程语言都能读写 JSON,它也成了 Web API、许多配置文件和无数数据导出的默认格式。JSON 的规则一页纸就能写完,但只要犯一个小错,比如多了一个逗号或用错了引号,整个文档就无法被程序读取。本文介绍 JSON 的规则、数据类型、最常见的错误以及修复方法。
JSON 长什么样
一个 JSON 文档就是一个值。这个值通常是用花括号括起来的对象,或者用方括号括起来的数组,里面再包含其他值:
{"name": "王小明", "age": 31, "languages": ["中文", "英语"], "manager": null}
规则很简单:
- 对象在
{ }中存放“名称–值”对。名称(键)是用双引号括起来的字符串,后面跟一个冒号和值。 - 数组在
[ ]中按顺序存放一组值。同一个数组里可以混合不同类型的值。 - 各项之间用逗号分隔,最后一项后面不加逗号。
- 字符串只能用双引号。在字符串内部,双引号写作
\",反斜杠写作\\,换行写作\n。 - 各项之间的空格、制表符和换行都会被忽略,所以同样的数据既可以写成一行,也可以分成多行。
- 没有注释、变量和函数。JSON 只表示数据。
JSON 由内容一致的两个标准定义:ECMA-404 和 RFC 8259。RFC 8259 还规定,在系统之间交换的 JSON 必须使用 UTF-8 编码,因此中文、日本語、한국어 和表情符号都可以直接写进去。
六种数据类型
| 类型 | 示例 | 规则 |
|---|---|---|
| 字符串 | "北京"、"caf\u00e9" | 只能用双引号,可以使用 \u 转义 |
| 数字 | 42、-3.5、1.2e6 | 不能有前导零、开头的 +、十六进制、NaN 或 Infinity |
| 布尔值 | true、false | 只能小写 |
| 空值 | null | 表示“没有值”,只能小写 |
| 对象 | {"id": 7} | 键是字符串,且不应重复 |
| 数组 | [1, "two", false] | 有顺序,类型可以混合 |
也要注意 JSON 缺少什么。它没有日期类型:日期通常存成 "2026-11-05T14:30:00Z" 这样的 ISO 8601 字符串,或者存成 Unix 时间戳,两者在 Unix 时间指南中都有介绍。它也没有单独的整数类型,包括 JavaScript 在内的许多解析器都把所有数字存为 64 位浮点数。因此,大于 9,007,199,254,740,991 的整数可能会悄悄改变:JavaScript 会把 {"id": 9007199254740993} 读成 9007199254740992。这就是很多接口把大 ID 当作字符串传递的原因。
JavaScript 的 JSON.stringify 也会悄悄改掉 JSON 无法表示的值:值为 undefined 的属性会被丢弃,NaN 和 Infinity 会变成 null。
常见错误及修复方法
JSON 解析器非常严格,只要有一处错误,整个文档就会被拒绝。错误信息会给出位置,但这个位置往往在真正的问题之后一点。比如多余的结尾逗号,报错的位置是它后面的那个右括号。最常见的错误如下:
| 问题 | 错误写法 | 正确写法 |
|---|---|---|
| 结尾多余的逗号 | {"a": 1, "b": 2,} | {"a": 1, "b": 2} |
| 单引号 | {'name': '小明'} | {"name": "小明"} |
| 键没有引号 | {name: "小明"} | {"name": "小明"} |
| 注释 | {"port": 8080 // 开发用} | {"port": 8080} |
| 缺少逗号 | [1, 2 3] | [1, 2, 3] |
| Python 风格的值 | {"ok": True, "x": None} | {"ok": true, "x": null} |
| 前导零 | {"code": 007} | {"code": "007"} |
| 未转义的反斜杠 | {"path": "C:\Users\ming"} | {"path": "C:\\Users\\ming"} |
| 字符串里直接换行 | 在引号内按了回车键 | "第一行\n第二行" |
| JavaScript 特有的值 | {"x": undefined} | {"x": null} |
有些错误的来源很明确。看到单引号、True 或 None,通常是有人直接打印了 Python 字典,而没有用 json.dumps 序列化:输出看起来像 JSON,其实不是。注释和结尾逗号往往来自用 JSONC 或 JSON5 写的配置文件,它们是部分工具接受的宽松变体,比如 VS Code 的 settings.json 和 TypeScript 的 tsconfig.json,但标准解析器两者都会拒绝。如果需要在纯 JSON 中留个说明,可以加一个普通字段,比如 "_comment"。
找错最快的方法,是把文档粘贴到 JSON 格式化工具中。它会在你输入时实时检查,显示第一个错误所在的行和列,点击格式按钮还会直接跳到出错的位置。
格式化与压缩
由于各项之间的空白会被忽略,同样的数据可以有两种写法。格式化(pretty-print)的 JSON 每个值占一行,嵌套的值会缩进,结构一目了然。压缩(minify)的 JSON 去掉了所有不必要的空格和换行。像 {"id": 1042, "name": "王小明", "roles": ["editor", "admin"]} 这样一条数据,压缩后是 57 字节,用两个空格缩进格式化后是 83 字节,但两者包含的数据完全相同。
| 格式化 | 压缩 | |
|---|---|---|
| 适合用于 | 阅读、调试、配置文件、代码评审 | API 响应、存储、嵌入其他文本 |
| 大小 | 较大,嵌套越深,缩进越多 | 尽可能小 |
| 版本控制 | 改动按行显示 | 只有一行,改一点整行都变 |
在网络传输中,两者的差距没有看上去那么大,因为服务器通常会用 gzip 或 Brotli 压缩响应,而重复的空格压缩效果非常好。比较实用的做法是:存储和传输时用压缩的 JSON,需要人阅读时再格式化。
JSON 与 CSV 的区别
CSV(逗号分隔值)是另一种常用的数据交换格式,尤其适合与电子表格打交道。把同样两条记录分别写成两种格式:JSON 是 [{"name": "小明", "city": "上海", "score": 92}, {"name": "Leo", "city": "Lyon", "score": 87}];CSV 则先是一行表头 name,city,score,然后每条记录一行:小明,上海,92 和 Leo,Lyon,87。
| JSON | CSV | |
|---|---|---|
| 结构 | 嵌套的对象和数组 | 只有行和列的平面表格 |
| 类型 | 字符串、数字、布尔值、null | 全是文本,由读取方推断 |
| 字段名 | 每个对象里重复出现 | 只在表头出现一次 |
| 表格类数据的大小 | 较大 | 较小 |
| 常用打开方式 | 代码编辑器、浏览器、API | Excel、WPS 表格、数据库 |
简单的判断标准:如果数据天然适合放进电子表格,而且大家会用 Excel 打开,就用 CSV;如果数据有嵌套部分(比如一个客户有多个地址)、需要真正的数据类型,或者要在程序之间传递,就用 JSON。也要留意 CSV 的弱点:包含逗号的值必须加引号;电子表格软件还可能把很长的数字(比如 18 位身份证号)显示成科学计数法,或者去掉 0755 这类区号开头的 0。CSV ⇄ JSON 转换器在两个方向上都能正确处理引号,嵌套的值会以 JSON 文本的形式写进同一个单元格。
哪里会用到 JSON
- Web API。大多数 REST API 都用 JSON 收发数据,并带上
Content-Type: application/json请求头。在浏览器开发者工具的“网络”面板里就能看到这些响应。 - 配置文件。JavaScript 项目的
package.json、PHP 的composer.json,以及许多应用的设置文件都是 JSON。 - 登录令牌。JSON Web Token(JWT)把小的 JSON 对象用 Base64url 编码后携带。这也是令牌几乎总以
eyJ开头的原因:{"后面跟一个字母,编码后就是这个样子。用 JWT 解码器可以把令牌还原成可读的 JSON。 - 日志和导出。JSON Lines 文件(
.jsonl)每行一个 JSON 对象,因此超大的文件也能逐行处理。 - 数据库和浏览器。PostgreSQL 有
json和jsonb列类型,网页应用也常把设置转成 JSON 字符串保存在本地存储中。
在代码中读写 JSON
| 语言 | 文本 → 数据 | 数据 → 文本 |
|---|---|---|
| JavaScript | JSON.parse(text) | JSON.stringify(data, null, 2) |
| Python | json.loads(text) | json.dumps(data, indent=2, ensure_ascii=False) |
| PHP | json_decode($text, true) | json_encode($data, JSON_PRETTY_PRINT) |
在 Python 中加上 ensure_ascii=False,“北京”这样的中文就会原样输出,而不是变成 \u5317\u4eac。另外,Python 的 json 模块默认会读写 NaN 和 Infinity,而其他解析器会拒绝它们;如果输出要交给其他系统,请给 json.dumps 传入 allow_nan=False。
相关工具
- JSON 格式化 – 校验 JSON,格式化或压缩,并找到出错的行。
- CSV ⇄ JSON 转换器 – 把表格数据转成 JSON,或把 JSON 转回 CSV。
- JWT 解码器 – 查看 JSON Web Token 里的 JSON。