宝塔拨测博客拨测技术博客
宝塔拨测

YAML 转 JSON 报错或结果不对?缩进、布尔值、注释和合并键一次讲清

把一份 YAML 贴进转换工具,结果通常只有两种:要么直接报错,要么转出来了但内容看着不对——DEBUG: on 变成字符串 "on",08080 变成 8080,注释和 << 合并键全不见了。

这两种情况都不是工具坏了。前者是缩进本身不合规,解析器读不下去;后者是 YAML 和 JSON 两种格式的表达能力不同,有些信息在 JSON 里根本没有对应写法。下面所有输入输出,都来自 宝塔的 YAML/JSON 互转工具 的一次真实运行,截图和数字取自同一次操作。

bad indentation of a mapping entry (3:8):报错怎么定位到那一行

先看一段典型写错的配置:

server:
  port: 80
   host: localhost

port 缩进 2 个空格,host 缩进 3 个,两者本该是同级键,实际对不齐。点「转换为 JSON」后,工具没有输出结果,而是在下方给出:

bad indentation of a mapping entry (3:8)

 1 | server:
 2 |   port: 80
 3 |    host: localhost
------------^

这三部分信息足够定位问题:(3:8) 是行号 3、列号 8,即第 3 行第 8 个字符处开始对不上;下面三行是出错行及其上下文;^ 指到具体位置。把第 3 行改成 2 个空格就能正常转换。

两个容易忽略的点:

  • 报错是英文的,因为解析来自 YAML 解析库本身(页面写明基于 js-yaml),不是工具自己编的提示,所以搜报错原文通常能找到同类案例。
  • 制表符不能用来缩进。YAML 规范规定缩进只能用空格,tab 只能出现在内容里。从网页、聊天窗口里复制配置时最常混进 tab,肉眼看着对齐,解析器一律不认。编辑器里打开「显示空白字符」再对齐一次,比逐行数空格可靠。

在宝塔 YAML/JSON 互转工具里转换一份 docker-compose 风格的 YAML,右侧输出 2 空格缩进的 JSON

on、yes、off 变成字符串:YAML 版本口径不同

同一批值一起转,结果很能说明问题:

YAML 输入 JSON 输出 说明
a: yes "a": "yes" 字符串,不是布尔
b: no "b": "no" 同上
c: off "c": "off" 同上
h: true "h": true 布尔
j: "true" "j": "true" 加了引号就是字符串
e: null / f: ~ null 两种写法都是空值

原因在版本口径:YAML 1.2 的 core schema 里布尔值只认 true / false(及 True、TRUE 这类大小写变体),yes/no/on/off 一律按普通字符串处理;把它们当布尔是 YAML 1.1 的老写法。工具按 1.2 的规则解析,所以 DEBUG: on 出来的是 "on"。

这个差别在程序里会出真问题:把 DEBUG: off 转成 JSON 后拿到的是非空字符串,if (config.debug) 判断为真,开关等于没关。要表达布尔就写 true/false,别指望 on/off,尤其当这份配置还会被别的语言、别的解析器读的时候。

数字被重新解释:08080 变 8080、0x10 变 16、1.0 变 1

YAML 输入 JSON 输出 原因
d: 08080 8080 十进制整数,前导零没有八进制含义,零被丢掉
g: 0x10 16 十六进制写法,值是 16;JSON 没有 0x 字面量
i: 1.0 1 JSON 不区分整数与浮点的书写形式,1.0 序列化成 1

这三条都不是精度丢失,是同一数值的另一种写法。真正会出事的是带前导零的编号:端口补位写的 08080、工号 007、电话号码这类,一旦被当成数字,零就回不来了。这类值在 YAML 里加引号写成 "08080",输出才会是字符串。

注释和 << 合并键:JSON 里装不下

YAML 支持注释,JSON 不支持——RFC 8259 定义的 JSON 语法里没有注释。所以转换时 # 服务定义 这类行被丢弃是必然结果,不是漏转。想在 JSON 侧留下说明,只能另存文档或加 _comment 之类的普通字段(那是你们自己的约定,不是标准)。

更容易被忽略的是锚点和合并键。实测输入:

defaults: &d
  timeout: 5
prod:
  <<: *d
  host: a.example.com

输出是:

{
  "defaults": {
    "timeout": 5
  },
  "prod": {
    "<<": {
      "timeout": 5
    },
    "host": "a.example.com"
  }
}

别名 *d 被展开了,但 << 合并没有生效:prod 下多出一个名字就是 "<<" 的键,期望合并进来的 timeout 没有提升到 prod 层级。合并键本身是 YAML 1.1 的扩展写法,不属于 1.2 core schema,转换时被当成普通键名处理。

所以带 << 的配置(docker-compose 这类配置里常见)要转成 JSON 给程序读,先把合并展开再转,或者干脆让程序直接读 YAML。否则字段会少一层,排查起来很费时间。

JSON 转 YAML:自动加引号是为了不改类型

反方向同理。把一行压缩 JSON 粘进去转成 YAML,输出是:

name: baota-probe
retries: 3
enabled: true
tags:
  - dns
  - http
empty: null
ratio: 0.5
version: '3.8'
text: 'a: b'

三个细节值得注意:

  • version: '3.8' 加了单引号。不加引号的话 YAML 会把它读成浮点数 3.8,JSON 里的字符串就丢了;加引号才能原样转回字符串。
  • text: 'a: b' 同样加引号。值里含「冒号 + 空格」在 YAML 里会被当成嵌套映射的写法,必须引起来。
  • 数组用 2 空格缩进加 - 展开,null 输出成 null(YAML 也认 ~),键顺序按 JSON 里的原始顺序保留。

同一工具切到 JSON → YAML 标签页,把压缩 JSON 转成带引号的 YAML

什么时候用在线工具,什么时候用命令

  • 一次性核对、贴工单、确认字段结构:这类场景用工具更直接。页面写明所有解析与序列化均在浏览器本地完成,据此内容不会提交到服务器;工具本身也有 JSON 格式化 和 XML 格式化,同一套页面里换标签即可。
  • 批量转换、接进 CI:用本机命令(yq、Python 的 yaml 库)更合适,能固定解析器版本、能进版本库、也能对合并键和布尔口径做统一处理。
  • 不管用哪种:含账号、密钥、内网地址的配置先脱敏。工具在本地跑,不等于可以随便贴。

一句话总结:报错看行号列号,结果不对先看类型和合并键。YAML 到 JSON 不是无损转换,注释、合并键、yes/on 这类 1.1 写法都会在中间被改写。

常见问题

bad indentation of a mapping entry 是什么意思?

意思是这一行的缩进破坏了映射(key: value)的层级关系,解析器在同一层级上读到了对不齐的键。报错末尾的 (3:8) 是行号和列号,表示第 3 行第 8 列开始对不上,下面还会打印出错行附近的三行内容和 ^ 标记。把这几行按同一层级对齐即可,不是数据本身有问题。

为什么 YAML 里的 on、yes 转成 JSON 后变成了字符串?

因为 YAML 1.2 的 core schema 里布尔值只认 true / false(含 True、TRUE 这类大小写变体),on/off/yes/no 都是普通字符串,工具按这个规则解析,所以 DEBUG: on 输出的是 "on"。早期 YAML 1.1 才把 yes/no/on/off 当作布尔,不同解析器按不同版本处理,跨工具转换时容易踩到。想表达布尔就写 true / false。

08080 转成 JSON 变成 8080,是丢数据了吗?

不是丢数据,是数字被重新解释。YAML 1.2 的整数按十进制的纯数字串解析,前导零没有八进制含义,08080 得到的就是 8080,再写进 JSON 就只剩 8080。如果这串数字有实际格式(端口号、编号、带前导零的号码),在 YAML 里加引号写成 "08080" 才能原样保留。

YAML 里的注释和 << 合并键,转成 JSON 后去哪了?

注释不会出现在 JSON 里——JSON 标准(RFC 8259)本身没有注释语法。<< 合并键实测不会被展开:输入 defaults: &d / prod: <<: *d,输出里 prod 下多出一个名为 "<<" 的键,值是别名指向的对象,而期望合并进来的 timeout 并没有提升到 prod 层级。带合并键的配置要转 JSON,先把合并展开再转。

JSON 转 YAML 为什么给 3.8 加了引号?

为了避免类型被改。YAML 里不加引号的 3.8 会被解析成浮点数,加引号才是字符串;工具为了让 JSON 里的 "3.8" 转回去还是字符串,会输出 version: '3.8'。同理,含冒号加空格的文本(如 a: b)也会被加引号,否则会被当成嵌套映射。

这些转换是在服务器上做的吗?敏感配置能贴吗?

工具页写明所有解析与序列化都在浏览器本地完成,内容不会提交到服务器。但页面毕竟是在线页面,含账号、密钥、内网地址的配置建议先脱敏再粘,或在本机用 yq、Python 的 yaml 库处理。

YAMLJSONYAML转JSON格式转换配置文件宝塔拨测