本文へスキップ
jsonbeautifiers
日本語

YAMLをJSONに変換

複数ドキュメントのストリームに対応。YAML 1.1の真偽値の罠を切り替えられます。

YAML
JSON

貼り付けたものがブラウザの外に出ることはありません。 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を使ってください。