雜記 › 技術筆記 › YAML 轉 JSON 後 0644 讀出 420:33 個布林詞裡 15 個不被當成布林

YAML 轉 JSON 後 0644 讀出 420:33 個布林詞裡 15 個不被當成布林

那份部署用的 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,介面上看不到行列資訊。

📎 相關文章

70 個位元組沒動,MIME 類型換了 13 次:副檔名才是唯一輸入 2026-10-23 轉 JPG 時透明被強制填底色:91 個品質檔位在 PNG 面前不參與編碼 2026-10-23 「全部清除」後還剩 553 位元組,「只去掉位置信息」連縮圖都沒動 2026-10-23 序號位數只是下限、重名時 (2) 插在後綴之前:批次重新命名 2000 個檔案的實測口徑 2026-10-23 兩條憑證指紋一條 95 個字元一條 64 個:差的 31 個全是冒號,摘要物件是整張 DER 2026-10-23