ガイド
YAML 1.1 と 1.2
最も高くついたYAMLのバグの中心には、国コードがある。
著者 yamltojsonfree · 公開日 · 更新日
Norway問題とは
YAML 1.1では、引用符のない y、yes、on、n、no、off は真偽値です — 大文字でも小文字でも先頭だけ大文字でも。そのためノルウェー(NO)を含む国コードのリストは、静かに false になります。エラーは出ません。ドキュメントは解析され、デプロイは成功し、下流のどこかで「真偽値は辞書のキーになれず文字列比較にも一致しない」という理由で、リストから国が1つ消えます。
2009年に公開されたYAML 1.2はこれを廃止しました。真偽値は true と false だけで、先の6語は通常の文字列です。YAML 1.1の他の暗黙変換も廃止されました — 022 はもう18を表す8進数ではなく、12:30 はもう750を表す60進数ではありません。
問題は、15年以上経った今も両バージョンが広く使われていることです。同じファイルが、開くツールによって2つの異なる意味を持ちうる — だからこそこのサイトのYAML→JSON変換ツールはデフォルトでYAML 1.2を使い、1.1では異なって読まれるドキュメント内の値をすべて警告します。
意味が変わる値の一覧
同じソースを、仕様の各バージョンで読んだ結果。
| YAMLソース | YAML 1.2での解釈 | YAML 1.1での解釈 | 安全な書き方 |
|---|---|---|---|
| country: NO | "NO" | false | country: "NO" |
| enabled: yes | "yes" | true | enabled: true |
| debug: off | "off" | false | debug: false |
| mode: 022 | 22 | 18 | mode: "022" |
| start: 12:30 | "12:30" | 750 | start: "12:30" |
| version: 1.10 | 1.1 | 1.1 | version: "1.10" |
最後の行に注目してください。1.10 は数値として解析されるため、どちらのバージョンでも末尾のゼロを失います。バージョン文字列は常に引用符で囲むべきです。
どのパーサーがどのバージョンを読むか
YAMLのバージョンを直接選ぶことはめったにありません — 使っているツールが依存するライブラリから継承します。エコシステムはおおむね次のように分かれています:
| パーサー | バージョン | 備考 |
|---|---|---|
| PyYAML (Python) | YAML 1.1 | Ansibleを含む多くのPythonツールのデフォルト。 |
| ruamel.yaml (Python) | YAML 1.2 | メンテナンスされているPyYAMLの後継。オプションで1.1も可。 |
| js-yaml v5 (JavaScript) | YAML 1.2 | このサイトが使うエンジン。以前のバージョンは1.1を読んでいた。 |
| go-yaml / sigs.k8s.io (Go) | ほぼ1.2 | Kubernetesが使用。互換性のため一部1.1の挙動を残す。 |
| SnakeYAML (Java) | YAML 1.1 | Spring Bootの設定読み込みのデフォルト。 |
| libyaml (C bindings) | YAML 1.1 | PyYAMLを含む多くの言語バインディングの基盤。 |
実務上の帰結:この表の片側で書かれ、反対側で読まれるファイルは、値が静かに変わりうるということです。YAMLをPythonで生成しKubernetesで消費する — とてもよくあるパイプラインです — なら、バージョンの境界を越えています。コードレベルの詳細はガイドコードでYAMLをJSONに変換するにあります。
両バージョンで安全なYAMLの書き方
3つの習慣でこの種のバグは丸ごと消えます。別のものと誤解されうる文字列はすべて引用符で囲む — 国コード、バージョン番号、先頭にゼロが付く値、コロンを含むものすべて。真偽値は true と false だけで書き、yes や on やその仲間は使わない。そしてファイルがツール間を渡る前に、バージョン依存の値を報告する変換ツールかバリデーターに通す — YAMLを明示的なJSONとして見るのが、意図しない型を見つける最速の方法です。
参考資料
- YAML 1.1仕様(2005年) — 上で説明した真偽値、8進数、60進数の形式
- YAML 1.2.2仕様 — 真偽値をtrueとfalseに限定するcoreスキーマ
- YAML 1.1の真偽値型 — YAML 1.1が真偽値として読む語の完全な一覧
- PyYAMLドキュメント — PythonにおけるYAML 1.1の挙動
- ruamel.yamlドキュメント — デフォルトはYAML 1.2、オプションで1.1
- js-yaml — このサイトで使うYAML 1.2エンジン
- sigs.k8s.io/yaml — Kubernetesが使うGoライブラリ