JSON 可以注释吗?— 先说结论
不行。标准 JSON(RFC 8259)在任何位置都不允许注释。在 .json 文件里写 // 或 /* ... */,严格解析器会直接报 "Unexpected token" 错误。这不是什么小限制,而是语法结构本身的约束。RFC 8259 第 2 节的语法定义中根本不存在接受注释的规则。但第 9 节明确写道:「JSON 解析器可以接受非 JSON 格式或扩展(MAY accept non-JSON forms or extensions)」——就这一句话,构成了 JSONC 和 JSON5 合法存在的基础。
如果你确实需要在 JSON 里留注释、临时禁用某个配置值、或者在字段旁边加说明,实际可用的方法有 4 种:
- JSONC — VS Code 采用的「带注释的 JSON」
- JSON5 — 包含注释、尾随逗号、宽松语法的正式 JSON 超集
- 在标准 JSON 里放
"_comment": "..."这样的占位字段 - 解析前把注释去掉
下面逐一讲每种方法的适用场景,以及怎么把带注释的 JSON 转回标准 JSON。
只需要快速结论的话,按用途这样选:
- 正在编辑 VS Code 或 Microsoft 系的配置文件,直接用 JSONC
- 新项目选配置格式,用 JSON5 更省心
- 解析器改不了,用
_comment字段或注释去除来应对
带注释的 JSON 不需要先去掉注释再粘贴。直接贴进 FormatArc JSON 格式化器,解析报错旁边会出现「自动修复」按钮。点击后显示要应用的规则列表,确认后 // 和 /* */ 就会被移除,得到干净的 JSON。所有处理都在浏览器内完成,数据不会离开你的设备。想让格式化后的结果更易读,可以参考 JSON 排版工具。
JSON 为什么不支持注释
JSON 的设计者 Douglas Crockford 曾公开说明,注释是被刻意从规范中拿掉的。原因是注释里嵌入解析指令的情况确实发生过,导致不同解析器之间互操作性被破坏。结果是 JSON 变成了这样:
- 小 — 语法一页能写完
- 无歧义 — 每个值的解读方式唯一
- 可移植 — 任何语言的内置解析器行为一致
代价是,你没法在 JSON 文件本身里留下「为什么设了这个值」的说明。这就是取舍,也是很多工具选择 JSONC 或 JSON5 这类超集的原因。如果想了解 JSON 本身的语法规则,可以参考 JSON 语法指南。
ECMA-404 与 RFC 8259:都禁止注释吗
都禁止。JSON 有两个标准定义其语法——ECMA-404在新标签页中打开 和 RFC 8259在新标签页中打开——两者的语法中都没有接受 // 或 /* */ 的规则。但两者对「如何处理不符合标准的输入」采取了完全不同的方向。
各自标准定义的内容
| ECMA-404(第 2 版,2017) | RFC 8259(Internet Standard 90,2017) | |
|---|---|---|
| 发布机构 | Ecma International | IETF |
| 规定范围 | 仅语法。第 1 节声明其唯一目的是定义有效 JSON 文本的语法 | 语法加上互操作性的语义约束 |
| 语法中是否包含注释 | 不包含 | 不包含 |
| 对不符合输入的处置 | 第 2 节规定「符合规范的处理器不应接受不符合规范的 JSON 文本(should not accept)」 | 第 9 节规定「JSON 解析器可以接受非 JSON 格式或扩展(MAY accept)」 |
| 与另一方的关系 | 第 3 节声明两个规范旨在描述同一语法语言,并明确「RFC 8259 规定的语义约束对本规范不具有规范性」 | 用不同的形式化方法规定相同的语法 |
| RFC 2119 关键字记法 | 不使用 | 使用 |
JSONC 和 JSON5 不构成规范违规的依据,正是 RFC 8259 第 9 节的这一句话。反过来,只看 ECMA-404 实现的解析器,拒绝带注释的输入才是符合规范的行为。这就是为什么同样是「遵循标准」的解析器,行为会出现分歧。
各解析器实际抛出的错误信息
大多数解析器从不会说出「注释」这个词。它们在第一个意外的 / 处停下,报告它本来期望找到的 token 名称,这使得错误信息很难直接追溯到一条多余的注释。下表展示了当解析器读取下面的文件时,各常见解析器实际输出的错误信息:
{
// server settings
"host": "localhost",
"port": 8080 /* default */
}
| 解析器 | 实际输出的错误信息 |
|---|---|
JSON.parse(Node.js / V8,Chrome 和 Edge 同引擎) | SyntaxError: Expected property name or '}' in JSON at position 4 (line 2 column 3) |
JSON.parse,注释在值之前 | SyntaxError: Unexpected token '/', "{"port": /* default"... is not valid JSON |
JSON.parse,注释在值之后 | SyntaxError: Expected ',' or '}' after property value in JSON at position 14 (line 1 column 15) |
Python json.loads | json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 2 column 3 (char 4) |
Python json.loads,注释在值之前 | json.decoder.JSONDecodeError: Expecting value: line 1 column 10 (char 9) |
jq | jq: parse error: Invalid numeric literal at line 2, column 5 |
| VS Code(文件被当作严格 JSON 处理时) | Comments are not permitted in JSON. |
上表中除 VS Code 外,其余 6 行均是在本站用上述代码片段实际复现的结果。VS Code 那一行是编辑器语言服务在界面上显示的文案,无法通过命令行复现。这里有 3 点值得注意:
- 只有 VS Code 直接点出了真正的问题。它的 JSON 语言服务会对每处注释标注「Comments are not permitted in JSON.」——但仅限于被当作严格 JSON 处理的文件。
settings.json或tsconfig.json里的同样注释会被当作 JSONC 解析而静默接受。 JSON.parse和json.loads责怪的是下一个 token,而不是注释。「Expected property name」或「Expecting value」意味着解析器在/处停下了——行号和列号指向注释本身,但错误信息里从未提及注释。jq把/误读为数值。「Invalid numeric literal」看起来像是数据问题,但在那个行列位置,通常是一条注释。
如果你看到这些错误信息但文件里看不到注释,说明语法的其他位置出了问题。可以结合 JSON parse 报错排查 逐行定位原因。
方法 1: JSONC — 带注释的 JSON
JSONC 是 Microsoft 在 VS Code 中采用的非正式扩展,在标准 JSON 基础上增加了两种注释:
// 行注释(到行尾)/* 多行注释 */
其他方面和标准 JSON 完全一样。典型的 JSONC 文件长这样:
{
// VS Code 启动时使用的主题
"workbench.colorTheme": "Default Dark Modern",
/* 编辑器全局设置
对所有语言生效 */
"editor.tabSize": 2,
"editor.formatOnSave": true
}
VS Code 的 settings.json、tsconfig.json、launch.json,以及大部分 Microsoft 系工具都默认使用 JSONC。Deno 的配置文件(deno.json)也是。
解析 JSONC
JSONC 不能用内置的 JSON.parse 直接读,需要专用库。
Node.js:
// npm install jsonc-parser
import { parse } from "jsonc-parser";
const data = parse(sourceText);
Python:
# pip install jstyleson
import jstyleson
data = jstyleson.loads(source_text)
VS Code 本体自带 JSONC 解析器,所以 settings.json 里写注释不会出问题。
只想用行注释(//)时的最小配置
实际工作中,注释几乎只用到行注释 //。需要 /* */ 块注释的场景很少,如果只是想给配置留一行说明,下面的最小配置就够。
在 VS Code 里,不需要把整个文件切成 JSONC 模式,只要把当前文件的语言模式改成「JSON with Comments」,// 注释就不会再标红。点窗口右下角的语言标识(显示「JSON」),选「JSON with Comments」即可。
但改语言模式只是消掉编辑器里的报错提示。真正读这个文件的程序如果解析器不支持 JSONC,运行时的 "Unexpected token" 错误依然存在。如果你要把带注释的 .json 传给 JSON.parse 或 json.loads,要么先经过 JSONC 专用库解析,要么用后面介绍的注释去除方法转成标准 JSON 再传。
方法 2: JSON5 — 有正式规范的扩展
JSON5(json5.org在新标签页中打开)是拥有正式规范的 JSON 超集,从 ECMAScript 5 借来了几个实用特性:
- 行注释和多行注释
- 对象和数组的尾随逗号
- 有效标识符的键可以省略引号
- 单引号字符串
- 行续接实现多行字符串
- 十六进制数、省略前导/尾随小数点、
Infinity/NaN
JSON5 文件的写法相当宽松:
{
// 预发布环境的功能开关
features: {
newDashboard: true,
legacyNotifications: false,
rateLimitRps: 0xff,
},
welcomeMessage: 'Hello, world',
/* 尾随逗号也允许 */
}
JSON5 有公开规范,所以几乎所有语言都有对应库。Node.js 用 json5在新标签页中打开,Python 用 pyjson5,Ruby 用 json5,都很常见。
JSONC 和 JSON5 该选哪个
看起来差不多,但特性不同:
| JSONC | JSON5 | |
|---|---|---|
| 正式规范 | 无 | 有(spec.json5.org在新标签页中打开) |
| 注释 | 支持 | 支持 |
| 尾随逗号 | 部分支持 | 支持 |
| 键省略引号 | 不支持 | 支持 |
| 单引号字符串 | 不支持 | 支持 |
| 生态 | Microsoft / VS Code | 独立,npm 等 |
选择标准:
- 正在编辑 Microsoft / VS Code 配置文件 → 你已经在用 JSONC 了,继续就好
- 新项目选配置格式 → 有规范文档可以指给工具的 JSON5 更省心
- 和标准 JSON 解析器的兼容性是第一位 → 保持纯 JSON,用下面的应对模式
各实现的支持情况
下表对比了 5 种常见的「非标准」特性在严格标准和两个超集中的表现。RFC 8259 列反映的是 RFC 8259在新标签页中打开 第 2 节的语法(与 ECMA-404在新标签页中打开 的规定一致)。JSON5 列反映公开的 JSON5 规范在新标签页中打开,JSONC 列反映 VS Code 文档中描述的 Microsoft 非正式扩展。
| 特性 | RFC 8259 允许? | JSONC | JSON5 |
|---|---|---|---|
行注释 // | 不允许 | 支持 | 支持 |
块注释 /* */ | 不允许 | 支持 | 支持 |
| 尾随逗号 | 不允许 | 可选 | 支持 |
| 键省略引号 | 不允许 | 不支持 | 支持 |
| 单引号字符串 | 不允许 | 不支持 | 支持 |
RFC 8259 列全是不允许,原因是第 2 节的语法中没有接受这些特性的 production rule。字符串必须用双引号包裹,键必须是字符串,对象或数组最后一个值后面不能跟元素。JSON5 的规范明确加入了 ECMAScript 5 的语法,所以 5 种都支持。JSONC 始终加上两种注释,但保留严格 JSON 的双引号键和字符串。尾随逗号的支持因解析器而异(JSONC 规范只说解析器可以接受(MAY),参考实现 jsonc-parser 的 allowTrailingComma 选项默认关闭),所以表里标的是「可选」而不是「支持」。
保持标准 JSON 的同时保留注释
如果第三方服务只接受标准 JSON,你可以不换解析器,改用字符串字段来留备注。
模式 1: _comment 字段
{
"_comment": "在上游超时就增加重试次数",
"retries": 3,
"timeout_ms": 5000
}
下划线开头的键在消费端通常被忽略,大多数代码也能把它当普通数据处理。这是最简单的应对方式。
模式 2: 每个字段旁边放注释键
{
"retries": 3,
"retries_comment": "再大就会超过上游超时时间",
"timeout_ms": 5000,
"timeout_ms_comment": "和负载均衡器的超时时间对齐"
}
文件会冗长一些,但哪条注释对应哪个字段一目了然。
模式 3: 外层元数据块
{
"$meta": {
"generated_by": "deploy.sh",
"purpose": "预发布环境的服务配置"
},
"service": {
"port": 8080,
"retries": 3
}
}
在最外层放一个元数据专用对象,正文负载不用动,上下文就保留了。
三种模式都会把实际数据写进文件,严格解析器照样能读。缺点是多出来的字段成了 schema 的一部分,消费端也需要知道这些字段的用途。
在 package.json 里写注释
package.json 是 npm 读取的标准 JSON 文件,不能写 // 或 /* */。写了的话 npm install 会在 JSON.parse 阶段直接失败。但 npm 会忽略不认识的最外层键,所以上面的应对模式可以直接套用。
最常用的方式是往约定的 "//" 键里写备注:
{
"//": "使用私有 registry 的配置,仅内网 CI 生效",
"name": "my-app",
"version": "1.0.0",
"scripts": {
"build": "tsc -p ."
}
}
同一个键在一个对象里只能出现一次,要留多条说明就用数组:
{
"__comments": [
"部署脚本不在这个文件里管理",
"engines 要和 CI 的 Node 版本保持一致"
],
"name": "my-app",
"engines": { "node": ">=20" }
}
npm 本身会忽略这些键,但 npm publish 发布的包里备注也会一并带出去。适合内部工具或私有仓库的 package.json。
.json 文件里用 // 键的注意点
"//" 键是社区约定,不是 JSON 规范的一部分。任何标准解析器都会把它当成一个普通的字符串键值对。也就是说:
- 你的 API 客户端如果按 schema 严格校验,可能会把
"//"当作未知字段拒绝或警告 - 如果文件被
jq或python -m json.tool格式化,"//"键会被原样保留 - 多个
"//"键不能在同一个对象里重复,需要多条说明时用数组
如果这个 JSON 会被外部服务消费,最好先确认对方是否容忍未知键。不确定时,用 $meta 外层块比散落多个 "//" 键更清晰。
如果想把配置意图留得更清楚,用后面介绍的注释去除模式,先按 JSONC 写再转成发布用标准 JSON 更稳妥。
去除注释后交给标准解析器
如果你手上是 JSONC 或 JSON5 文件,但需要交给标准 JSON 解析器,有两条路:用 JSONC/JSON5 库解析后再序列化为 JSON,或者用正则表达式去掉注释。
Node.js 用 json5 包的示例:
// npm install json5
import JSON5 from "json5";
import fs from "node:fs";
const source = fs.readFileSync("config.json5", "utf8");
const data = JSON5.parse(source);
fs.writeFileSync("config.json", JSON.stringify(data, null, 2));
最小正则也能去掉注释。但字符串字面量里包含 // 的话会被误切,不是你自己管理的文件就别用。
const stripped = source
.replace(/\/\/[^\n\r]*/g, "")
.replace(/\/\*[\s\S]*?\*\//g, "");
const data = JSON.parse(stripped);
稳妥的做法始终是走专用解析器。
批量处理时的注意事项
项目中如果有多个 JSONC 配置文件需要统一转为标准 JSON,建议写一个小的转换脚本,统一用 jsonc-parser 或 json5 库解析后输出。正则表达式方案在单文件、内容可控时够用,但批量处理时风险急剧上升——任何一个字符串里出现 // 或 /* 都会导致数据损坏。
在 Python 中处理 JSONC / JSON5
Python 标准库 json 只能读严格 JSON,JSONC 或 JSON5 直接传给 json.loads 会在注释行报错。应对方法有两种。
想轻量处理的话,用标准库 re 去掉注释再 json.loads:
import json, re
def load_jsonc(text):
text = re.sub(r"//[^\n]*", "", text) # 行注释
text = re.sub(r"/\*.*?\*/", "", text, flags=re.S) # 块注释
return json.loads(text)
with open("tsconfig.json", encoding="utf-8") as f:
config = load_jsonc(f.read())
这个正则会连字符串字面量里的 // 也切掉,不是自己管的文件别用。
要正确读取尾随逗号、省略引号等特性,用专用库。JSONC 用 jstyleson,JSON5 用 json5 或 pyjson5。
# pip install jstyleson json5
import jstyleson
import json5
config = jstyleson.load(open("settings.json", encoding="utf-8")) # JSONC
data = json5.load(open("config.json5", encoding="utf-8")) # JSON5
只读配置的话正则够用,但输入不可信、或者可能混有尾随逗号和单引号的情况,专用库更稳。
如果你的项目同时依赖多个 JSON 文件且其中一部分是 JSONC(例如从 CI 流水线自动生成的配置),建议统一在入口处用 jstyleson 或 json5 解析,避免散落在各处的 re.sub 正则不一致。
用 FormatArc 整理并检查结果
转成标准 JSON 之后,贴进 FormatArc JSON 格式化器 查看格式化结果。如果解析器还在报错(典型的如 Unexpected token /),大概率是注释还没清干净。
FormatArc 本身使用浏览器内置的 JSON.parse,但解析失败时可以走自动修复。步骤:
- 带注释的 JSON 直接贴进 JSON 格式化器
- 点报错旁边的「自动修复」
- 确认要应用的规则列表,点「应用」
- 查看、验证、复制格式化后的 JSON
自动修复处理 4 种:// 行注释、/* */ 块注释、尾随逗号、连续逗号。字符串里的 //(比如 "https://example.com")不在处理范围内,URL 不会被破坏。JSON5 的单引号或无引号键不在自动修复范围内,这种情况先用 JSON5 库解析再贴进来。所有处理在浏览器内完成,数据不离开你的设备。


常见问题
RFC 8259 对 JSON 注释是怎么规定的?
RFC 8259 从头到尾没有出现过「comment」这个词。第 2 节的 JSON 语法(ABNF)定义了两个 token 之间能出现的空白只有 4 种:space / tab / line feed / carriage return(ws = *( %x20 / %x09 / %x0A / %x0D )),不存在接受 // 或 /* */ 的 production rule,所以严格解析器会报 "Unexpected token" 错误。第 9 节则明确写「JSON 解析器可以接受非 JSON 格式或扩展(MAY)」,这是 JSONC 和 JSON5 不构成规范违规的依据。
.json 文件里能写 // 或 /* 注释吗?
不能(如果这个文件会被严格解析器如 JSON.parse、json.loads、encoding/json 读取)。第一个注释处就会报 "Unexpected token"。把扩展名改成 .jsonc 并用 JSONC 支持的读取器,或者迁移到 JSON5。
能只注释掉 .json 文件里的一行吗?
不能。JSON 里根本没有行注释语法,想在某行前加 // 来临时禁用一个值,那行本身就是语法错误。想临时禁用一行的写法有 3 种:直接删掉那个键、靠版本管理的历史记录找回;把要禁用的键改名为带 _disabled_ 前缀的键来搁置;把文件切到支持 JSONC 的环境里正式注释。三选一。
VS Code 为什么在 settings.json 里写注释不会报错?
因为 VS Code 把 settings.json 这类文件当作 JSONC 而不是严格 JSON 来处理。内置解析器接受 // 和 /* */。其他用 JSON.parse 读同一个文件的编辑器,如果不理解 JSONC,就会报错。
JSON5 是标准吗?
JSON5 在 spec.json5.org在新标签页中打开 有公开规范,但不是 IETF 或 ECMA 的正式标准。在开发工具生态中支持很广泛,但不能替代 API 或通信协议中使用的 RFC 8259 JSON。
FormatArc 支持 JSONC 或 JSON5 吗?
JSONC 可以通过自动修复处理。带注释的 JSON 直接贴进去,走「自动修复」到「应用」,// 和 /* */ 就会被移除,尾随逗号和连续逗号也一并修正。JSON5 的单引号或无引号键不在自动修复范围内,这种情况先用 JSON5 库解析再贴进来。
如果一定要注释,是不是得用 YAML?
YAML 用 # 原生支持注释。如果主要目的就是加注释、且格式可以换,YAML 对配置文件来说往往是更合适的选择。YAML 和 JSON 的语法差异与选型,见 YAML 和 JSON 的区别。FormatArc 提供 YAML 和 JSON 之间的互转工具。
TOML 呢?
TOML 也用 # 写注释,且从设计之初就面向配置文件场景,key 不需要引号就能保持可读性。如果你的文件主要是人工编辑、而不是服务间传递数据,TOML 比 JSON 更合适。但如果需要在多个服务之间做数据交换,TOML 解析器的普及程度远不如 JSON,此时还是用 JSON 配合注释去除更稳妥。
总结
- 标准 JSON 不支持注释,这是规范刻意的约束
- JSONC 允许
//和/* */,VS Code 生态广泛使用 - JSON5 是有规范的超集,支持注释、尾随逗号、宽松语法
- 解析器改不了的话,用
_comment字段或外层元数据块应对 - 带注释的 JSON 直接贴进 JSON 格式化器,用自动修复去掉注释和尾随逗号后即可整理验证