YAMLをJSONに変換
複数ドキュメントのストリームに対応。YAML 1.1の真偽値の罠を切り替えられます。
貼り付けたものがブラウザの外に出ることはありません。 connect-src の許可リストにより、これは約束ではなくブラウザによる保証になっています。 自分で確かめる
YAMLをJSONに変換します。複数ドキュメントに対応し、もっとも混乱を招く型付けルールを切り替えるスイッチも用意しています。
問題が起きるのはこちらの方向で、そのほとんどは、ほかのツールがYAMLのどのバージョンを実装しているかに帰着します。
ノルウェー問題の実演
YAML 1.1では、クォートなしのno、yes、on、off、y、nは真偽値です。したがって、ノルウェーを表すNOを含む国名リストはfalseとして解釈されます。YAML 1.2では、これらはただの文字列です。
これが重要なのは、この分断がエコシステム全体を貫いているからです。PyYAML、RubyのPsych、いくつかの古いツールは1.1を実装しています。js-yaml、Goのyaml.v3、そして現代的なパーサーのほとんどは1.2を実装しています。同じファイルが、読む側によって別の意味になるのです。
ここで型付けルールを切り替えると、値が目の前で変わるのを確認できます。js-yaml 5.4.1での実測では、`a: no`はYAML 1.2で文字列"no"、YAML 1.1で真偽値falseになります。1.2モードであっても、クォートのないnoやyesを見つけたときにはこのツールが警告します。パイプラインの次のツールが同じ解釈をするとはかぎらないからです。
YAMLにあってJSONにないもの
- コメント
- 永久に失われます。JSONにはコメントの構文がありません。YAMLを手で管理しているなら、これは一方通行の変換です。
- アンカーとエイリアス
- その場に展開されます。アンカーを5回使っているドキュメントはJSONでは5つのコピーになり、サイズがかなり大きくなることもあります。
- タイムスタンプ
- YAMLは日付に見えるスカラーを実際の日付として解決します。JSONには日付型がないため、ISO 8601の文字列として書き出します。最初から素の文字列のままにしたい場合は、JSONの型付けルールを選んでください。
- 文字列でないキー
- YAMLでは数値やシーケンスさえマッピングのキーにできます。JSONではできないため、文字列に変換します。
- .infと.nan
- JSONに相当するものがないため、nullになります。
- 複数のドキュメント
- ---で区切られたストリームには複数のドキュメントが含まれます。既定では最初のものだけを変換し、ほかにもあったことを通知します。「すべてのドキュメント」を有効にすると配列として得られます。
タブ文字
YAMLはインデントにタブを使うことを、例外なく完全に禁じています。YAMLでもっともよくあるエラーであり、原因はエディタがタブを挿入する設定になっていることです。このツールはパースを試みる前にタブについて警告します。どのパーサーでも、その場合に出るメッセージが役に立たないからです。
How to do this in code
コードでの変換と、それに付随する安全性の話。
py Python
PyYAMLはYAML 1.1を実装しているので、"no"がFalseになるのはここです。
import yaml, json
# safe_load, never load. yaml.load can construct arbitrary Python
# objects and has been a real remote-code-execution vector.
data = yaml.safe_load(text)
print(json.dumps(data, indent=2, default=str))
# default=str handles the datetime objects PyYAML produces for
# date-shaped scalars, which json.dumps otherwise refuses. sh Shell
yq -o=json eval . input.yaml > output.json
# Every document of a multi-document stream
yq -o=json eval-all '[.]' input.yaml js JavaScript
js-yamlはYAML 1.2を実装しているので、ここでは"no"は文字列のままです。
import { load, loadAll } from 'js-yaml';
const data = load(text); // YAML 1.2 core schema
const docs = loadAll(text); // multi-document stream go Go
import "gopkg.in/yaml.v3"
var v any
if err := yaml.Unmarshal(data, &v); err != nil { return err }
out, _ := json.MarshalIndent(v, "", " ") よくある質問
- 値がtrueやfalseになってしまったのはなぜですか?
- YAML 1.1のパーサーで読んでいるからです。そこではno、yes、on、off、y、nが真偽値です。値をクォートするか、YAML 1.2のパーサーを使ってください。上の型付けルールを切り替えれば、自分のドキュメントで違いを確認できます。
- コメントはどこへ行ったのですか?
- JSONにはコメントがないため、削除されます。回避方法はありません。YAMLが自分で管理しているファイルなら、そちらを正とし、JSONは生成物として扱ってください。
- Pythonでyaml.loadが安全でないのはなぜですか?
- ドキュメント内のタグから任意のPythonオブジェクトを生成できてしまうため、信頼できないYAMLをパースすることが、それを実行することと同じになるからです。必ずsafe_loadを使ってください。