杂记 › 技术笔记 › YAML 转 JSON 后 11.10 变成 11.1:33 个布尔词与 8 处文案不符的实测

YAML 转 JSON 后 11.10 变成 11.1:33 个布尔词与 8 处文案不符的实测

那份部署用的 YAML 里有三行很普通的东西:image: 11.10、debug: on、mode: 0644。我把整份文件丢进转换器要一份 JSON,再转回 YAML 提交,diff 里跳出三处谁都没改过的东西:11.10 少了末尾的零、on 换成 true、0644 变成四百多。这不是转换器坏了,而是 YAML 的隐式类型解析在动手:它不接受「这段文字就是这段文字」,每个标量都要先猜一次类型,再决定怎么写进 JSON。

踩过的坑分成三类:被当成布尔或数字的字符串、被静默吃掉的重复键,以及锚点与多文档这类引用结构。我在 YAML 与 JSON 互转 上把一批标量逐条喂进去,把「输入怎么写」和「输出什么类型」抄成一张对照表。

一张对照表:哪些标量会被换掉

输入写法默认读成的值换档或加引号之后
yes / No / ON / off布尔 true / false严格档全部退回字符串
y / n / yEs / oFf原样字符串混合大小写一律不判布尔
true / TRUE / False布尔两种档位都判布尔
11.10 / 2.0 / 0.5011.1、2、0.5加引号才保住末尾的零
11.01 / 1.2.311.01 是数字,1.2.3 是字符串三段点分不会被当成数字
007 / 0x1F / 0o17 / 0b1017、31、15、5显式前缀与档位无关
08 / 09整串退回字符串严格档按十进制读成 8、9
1_000 / .5 / 1e31000、0.5、1000严格档 1_000 变字符串
2026-10-23 / 12:30:00 / 2026-13-45全是字符串,0 条警告不转 Date,也不校验日期合法性
.inf / .nan带引号的 "Infinity"、"NaN",各挂 1 条警告JSON 本身没有这两个字面量
90071992547409939007199254740992越过安全整数后末位被舍入

布尔词表:33 个词里 15 个不被当成布尔

默认档把 YAML 1.1 的布尔词表整批扫一遍:33 个候选写法里 9 个判 true、9 个判 false、15 个保持字符串。收窄的两处:单字母 y 和 n 不算布尔,混合大小写的 yEs、oFf 也一律原样;带前导或尾随空格的 true 同样不判。写的时候差一个字母,读出来的类型就换了档。

这条差异落在配置文件上很直观:a: no 与 b: on 出来是 "a": false, "b": true,而 c: y 与 f: n 仍是字符串。反方向做 JSON 转 YAML 时,危险键名会被补上引号——"on": 1 落成 'on': 1,键位不会被下一次读回时吃掉。

前导零不是十进制:0644 读出 420

默认档按 YAML 1.1 读前导零:007 是 7,017 是 15,011 是 9,0644 是 420,00 是 0。文件权限、分片编号、工单号这类字符串最容易被这一条改动。更怪的是它只在八进制位合法时才换算:08 和 09 因为出现非法八进制位,整串退回字符串。

显式前缀与档位无关:0x1F 是 31、0o17 是 15、0b101 是 5;残缺的 0x、0b 原样留成字符串。下划线也被直接删掉,1_000 变 1000,1_0.5 变 10.5。

界面右上角那枚严格档开关不是只管布尔,它同时改写了 6 处数字读法:011 从 9 变 11、017 从 15 变 17、08 与 09 从字符串变 8 和 9、1_000 从 1000 变回字符串、1.5e3 从字符串变 1500。改的是整套读法,不是只有 yes/no 那几个词。

哪些字段最容易中招

比例不算吓人,会出问题的字段有共同点:看着像数字的字符串(版本号、端口、权限位)、看着像英文的开关值(on、no、off),以及看着不是日期的日期——12:30:00 这类六十进制写法一律保持字符串,不会被乘 60。

33 个布尔候选写法 判为真 9 个 判为假 9 个 保持字符串 15 个

图 1:布尔词表扫描的 9 / 9 / 15 归属

精度这条线画在哪里

浮点保留 17 位有效数字,0.30000000000000004 这类值原样输出,不会被圆到 0.3。断点是整数的安全上界:越过 9007199254740991 就掉精度,9007199254740993 出来是 9007199254740992,末位被双精度舍掉,这种丢法没有任何提示。超长 ID、雪花号先加引号再转。

输入侧没有长度上限,一份 20000 行、397779 字节的 YAML 走完是 85 毫秒、产出 537782 字节。反方向也不会把大整数写成科学计数,9007199254740992 写回 YAML 仍是那一长串。

重复键:覆盖发生在前,提示只在一半的场景里

b: 1 / a: 2 / b: 3 三行出来是 "b": 3 和 "a": 2,值取后面那次,键位仍按首次出现的顺序排。合并会挂一条警告:「第 3 行:键 b 重复出现,后面的值覆盖了前面的值」。同一个键写 n 行,警告条数正好是行数减 1,最后一条落在发生覆盖的那行。

两处例外。带引号的键与裸键算同一个键:"k": 1 配 k: 2 合成一个键并给 1 条提示。写成流式集合则不一样:a: {k: 1, k: 2} 覆盖照样发生,结果只剩 "k": 2,警告却是 0 条。靠警告列表判断有没有被覆盖并不可靠。

块映射的重复键 3 行 2 条 4 行 3 条 5 行 4 条 流式 1 行集合里重复 0 条提示 值仍被覆盖

图 2:重复键行数与警告条数

锚点全部展开,合并键只补空

JSON 没有锚点这个概念,转过去的那一刻引用就被摊平成实际值。a: &anchor hello 配 b: *anchor 出来是两个 "hello";别名指向序列时按深拷贝落进结果。

合并键 << 的优先级容易记反:它只补空,不覆盖。基里给 a: 1、c: 3,子节点自己写 c: 9,展开后 a 补进来、c 仍是 9。列了多个锚点时,后写出的先落键,本节点的值照样赢。

s: &s [1, 2]
d:
  <<: *s

合并键指向序列而不是映射时,结果是空对象,并给 2 条同样的「第 3 行:合并键 << 引用的不是映射,已跳过」。三类失败用法共用另一条文案:前向引用(先 *a 后 &a)、引用没定义过的名字、循环别名,都停在「引用了未定义的锚点」上且行列坐标齐全。

Q:版本号这类值怎么保持字符串?

加引号最稳,"11.10"、"no" 走完全程形状不变。布尔词那半边还能勾严格档退回字符串,但这一档同时改了 6 处数字读法,划不来。

Q:缩进里混了 Tab 会被指出来吗?

只有缩进位置的 Tab 会被拦,报错带行列并提示换成空格;写在值中间或流式集合里的 Tab 被当空白跳过,一条提示都没有。和同级混排时,报的还是「缩进不一致」。

Q:一次能转多份文本吗?

多文档是唯一批量形态:用 --- 分隔并勾选「多文档」开关,输出变顶层数组,空文档落成 null 元素。没勾开关时 2 个文档会被整条拒绝,这句报错坐标固定是 line=0 col=0,界面上看不到行列信息。

📎 相关文章

后缀决定 MIME:同一份 70 字节 PNG 头改出 18 个后缀、13 种类型和 5 个未知 2026-10-23 转 JPG 时透明被强制填底色:91 个质量档位在 PNG 面前不参与编码 2026-10-23 「全部清除」后还剩 553 字节,「只去掉位置信息」连缩略图都没动 2026-10-23 序号位数只是下限、重名时 (2) 插在后缀之前:批量重命名 2000 个文件的实测口径 2026-10-23 证书指纹实测:1159 字节 DER 与 3 种摘要、4 种写法 2026-10-23