YAML vs JSON vs TOML — 設定ファイルのフォーマットをどう選ぶか
アプリの設定 / CI/CD / インフラ定義で YAML / JSON / TOML をどう使い分けるかを、可読性 / 表現力 / パースの厳密さ / ツール対応 で比較。YAML の罠 (Norway 問題など) も整理します。
どの軸で選ぶか — 4 つの判断基準
設定フォーマット選びは「結局どれが読みやすいか」だけでは決められません。判断の基準は 4 つです。可読性 はチームの誰がいつ編集するか (アプリ開発者か / SRE か / 非エンジニアか) で変わります。表現力 はネスト・配列・コメント・複数行文字列など、設定で表現したい構造をそのまま書けるかどうか。パースの厳密さ は「意図しない型変換」「末尾カンマの可否」「空白の有意性」など、人間が踏みやすい罠の少なさを左右します。ツール・エコシステム対応 は使う側のランタイム (Kubernetes / Rust Cargo / Node.js / Python) がどれを第一級で受け取るかで実質的に決まります。
「YAML は読みやすい」「JSON は厳密」「TOML はモダン」のような一面的な評価ではなく、4 軸のどれが致命的かで答えが変わります。Kubernetes マニフェストなら表現力と既存エコシステム、API レスポンスなら厳密さと相互運用、Cargo パッケージ宣言なら可読性とエコシステム、という具合に。
3 フォーマットの比較表
| 項目 | YAML | JSON | TOML |
|---|---|---|---|
| 標準仕様 | YAML 1.2 (2009) | RFC 8259 (2017) | TOML 1.0.0 (2021) |
| 開発年 | 2001 | 2001 | 2013 |
| コメント | 可 (#) | 不可 | 可 (#) |
| 末尾カンマ | 該当なし | 不可 | 配列のみ可 |
| ネスト記法 | インデント有意 | 中括弧 | テーブル [a.b.c] |
| 配列 | - または [...] | [...] | [...] |
| 複数行文字列 | ` | >` で可 | 不可 (\n で表現) |
| 型推論 | 暗黙 (罠あり) | 明示 | 明示 (日付型まで含む) |
| 主要採用例 | Kubernetes / GitHub Actions / Docker Compose | Web API / package.json / VS Code | Cargo.toml / pyproject.toml / Hugo |
| ファイル拡張子 | .yaml .yml | .json | .toml |
YAML の最大の罠は Norway 問題 と呼ばれる暗黙の真偽値変換です。YAML 1.1 では NO / yes / on / off が自動的に真偽値に変換されるため、国コード "NO" (ノルウェー) を引用符なしで書くと false になります。YAML 1.2 で挙動は厳格化されましたが、PyYAML をはじめ多くのパーサが 1.1 互換モードを既定にしているため、いまも実害があります。インデントずれ (タブとスペースの混在、項目間のインデント不一致) も頻発します。
JSON は規格が短く挙動が予測しやすい反面、コメントが書けない、末尾カンマで構文エラーになる、複数行文字列を \n で書き下す必要がある、といった「設定ファイルとしての書きにくさ」があります。tsconfig.json などで JSONC (コメント付き JSON) や JSON5 が使われるのはこのためです。
TOML は INI ファイルに近い見た目で、テーブルヘッダ [server.database] のおかげで深いネストでもインデントに頼らずに書けます。Rust Cargo や Python の pyproject.toml で標準採用されてから一気に普及しました。一方で配列の中に複数のテーブルを並べる ([[products]]) ような構造は記法に独特さがあり、深く動的なツリーには向きません。
ユースケース別の推奨
Kubernetes マニフェスト / GitHub Actions / Docker Compose / Ansible Playbook: YAML 一択。ツール側がそもそも YAML しか受け付けないことが多く、選択の余地はありません。代わりに 暗黙の型変換に対する自衛 が必要です。バージョン番号 1.10 を引用符なしで書くと float の 1.1 になりますし、NO / ON も罠です。基本ルールは「文字列にしたいものは必ずクォートで囲む」。
Web API レスポンス / 設定ファイルでもアプリ間でやりとりするもの / package.json: JSON。仕様が最小で、ほぼすべての言語に標準ライブラリのパーサがあり、機械が読む経路では事故が一番少ない。手書き編集が必要なら JSONC や JSON5 にしてもよいですが、保存時に標準 JSON へ正規化する 運用がおすすめです。
Rust Cargo / Python pyproject / Hugo の設定 / 個人ツールの設定ファイル: TOML。1 階層〜2 階層のネストで収まる構造に向いており、コメントが自然に書けます。pyproject.toml のように「ビルド設定 + 依存関係 + ツール設定」をひとつのファイルに乗せる用途でも、テーブルヘッダで論理的に区切れて読みやすい。
VS Code / Neovim / ESLint の設定: JSON または JSON5 / JSONC。エディタ側が拡張 JSON を許容しているので、コメントや末尾カンマを使ってしまってよい。プロジェクト共有時は CI で JSON にダウンキャストする か、最初から .json5 拡張子で明示するとレビューしやすくなります。
nosend-tools で完結する変換と落とし穴
3 フォーマット間の変換は手書きでやると型変換ミスを起こしがちです。yaml-json-convert は YAML ↔ JSON を相互変換し、整形済みの出力を返します。TOML との相互変換が必要なら toml-json-convert を経由して JSON をハブにする 2 段構成が安定です。出力を後段でクリーンに整えたいときは json-format でインデントとキー順を統一できます。
ブラウザだけで変換することの最大の利点は、設定ファイルに含まれがちな機微情報 (本番 DB のホスト名・内部 API のエンドポイント・サービスアカウント名) を一切ネット越しに送らないことです。アップロード型のオンラインコンバータは入力を運営側ログに記録できる構造で、規約で明示的に解析・改善利用に組み込んでいるケースもあります。実装は GitHub で公開しており、DevTools の Network タブで「変換中にどこにも送信されていない」ことを目視確認できます。
最後に YAML を扱うときの自衛として、信頼できない YAML を yaml.load で読み込まない ことを覚えておいてください。PyYAML の yaml.load は任意 Python オブジェクトの構築を許す既定挙動で、過去に多数の RCE 脆弱性が報告されました。yaml.safe_load を使う、Go なら gopkg.in/yaml.v3 の strict モード、Node.js なら js-yaml の safeLoad 系 API を使うのが基本ルールです。