那份部署用的 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.50 | 11.1、2、0.5 | 加引號才保住末尾的零 |
11.01 / 1.2.3 | 11.01 是數字,1.2.3 是字串 | 三段點分不會被當成數字 |
007 / 0x1F / 0o17 / 0b101 | 7、31、15、5 | 顯式前綴與檔位無關 |
08 / 09 | 整串退回字串 | 嚴格檔按十進位讀成 8、9 |
1_000 / .5 / 1e3 | 1000、0.5、1000 | 嚴格檔 1_000 變字串 |
2026-10-23 / 12:30:00 / 2026-13-45 | 全是字串,0 條警告 | 不轉 Date,也不校驗日期合法性 |
.inf / .nan | 帶引號的 "Infinity"、"NaN",各掛 1 條警告 | JSON 本身沒有這兩個字面量 |
9007199254740993 | 9007199254740992 | 越過安全整數後末位被捨入 |
布林詞表: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。
圖 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 條。靠警告清單判斷有沒有被覆蓋並不可靠。
圖 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,介面上看不到行列資訊。