こんにちは、教育系エンジニアのひらまつ(@hiramatsuu)です。
書籍「ゼロからわかる Linuxコマンド200本ノック(技術評論社)」の著者。Udemy受講者8万人。
プログラミング教育をメインに活動するエンジニアとして、動画教材の作成・技術書の執筆・学習アプリの開発などを行なっています(詳しくはこちら)。
本記事では、YAML1.2(執筆時点の最新バージョン)の基本について解説した記事です。
YAMLはデータ交換の用途だけでなく、さまざまな環境で設定ファイルとして使われている、とても重要なデータ形式です。
ですが、YAMLについてしっかりと理解せずに、雰囲気で使っている方も多いのではないでしょうか。
本記事は、YAMLの概要を素早く理解したい、YAMLをなんとなくで使っているレベルから抜けたい、というような方に、特に参考になる記事になっていると思いますー。
簡単なまとめ
- YAMLの最大の特徴は、可読性の高さ。JSONやXMLと比較して、最も読みやすいデータ形式。
- YAMLの可読性を支えているのは、ブロックスタイルというインデントで構造を表現する表記方法。ブロックスタイルを使うことで、可読性を下げるカッコなどの記号の使用を、最小限にすることができる。
- また、コメントやアンカー・エイリアスという、JSONにはない、可読性や保守性に貢献する機能も用意されている。
- 一方で、可読性などの、人間にとっての使いやすさを重視した結果、内部構造がJSONと比較して複雑になっており、パースの速度でJSONに劣る可能性がある、というデメリットはある。
- YAMLでは、スカラ(単一の値)・マップ(連想配列のようなもの)・シーケンス(配列やリストのようなもの)という3つを使って、データを表現する。
- YAMLはJSONの上位集合であり、有効なJSONデータはすべて、有効なYAMLデータでもある。またYAMLでは、フロースタイルという、JSONのような表記方法も可能。
- YAML1.1→YAML1.2でいくつかの変更があったため、注意が必要。
YAMLとは?
- YAMLは、JSONなどと同じ、データシリアライゼーション言語の一つ。人間にとって親しみやすいだけでなく、現代のプログラミング言語における、普遍的・日常的なタスクでうまく機能するようにデザインされている。
- YAMLのアクセントは「camel」と同じ。
- 「YAML Ain’t Markup Language(YAMLはマークアップ言語ではない)」という、再帰的な頭字語が由来になっているとされる。
- 本記事執筆時点では、2021−10−01にリリースされた「v1.2.2」が最新バージョン。
- YAMLは、フレームワークやCI/CDなど、様々な用途で設定ファイルなどとして利用されている。例えば、CircleCIの.circleci/config.ymlや、Flutterのpubspec.yamlなど。
- YAMLファイルの拡張子は、.yamlと.ymlがあり、環境によって異なる。
YAMLの目指すゴール
YAMLには以下のようなゴールが設定されている(数字が小さいほど優先度が高い)。
- YAMLは、人間にとって簡単に読めるべきである。
- YAMLデータは、プログラミング言語間で移植可能であるべきである。
- YAMLは、動的言語のネイティブデータ構造に合致すべきである。
- YAMLは、汎用ツールをサポートするための一貫したモデルを持つべである。
- YAMLは、ワンパス処理をサポートすべきである。
- YAMLは、表現力豊かで、拡張可能であるべきである。
- YAMLは、実装が簡単で、使いやすいものであるべきである。
XMLやJSONとの違いは?
XML(eXtensible Markup Language)との比較
- 名前の由来にもなっている通り、「マークアップ言語ではない」というのが、YAMLの大きな特徴の一つ。
- XMLのようなマークアップ言語と違って、タグなどを使う必要がなく、簡潔な表現が可能であり、人間にとって読みやすい・書きやすい形式になっている。
- これはYAMLが、XMLなどの問題点を元に開発されているから。
JSON(JavaScript Object Notation)との比較
- YAMLもJSONも人間にとって読みやすい形式を目指している、という点では同じだが、優先順位が異なる。
- JSONの最重要の目標は、シンプルさと普遍性。それゆえにJSONは、生成とパースが容易である一方で、人間にとっての読みやすさを犠牲にしている。
- 一方でYAMLは、人間にとっての読みやすさと、任意のネイティブデータ構造のシリアライゼーションをサポートすることを、最優先の設計目標としている。それゆえにYAMLは、非常に読みやすいファイルを実現しているが、生成とパースがより複雑になっている。
- また、YAML 1.2から、YAMLはJSONの完全な上位集合になった(言い換えると、JSONはYAMLの部分集合になった)。これはつまり、JSON形式のデータは全て、有効なYAMLデータでもあるということ。
YAML/JSON/XMLの比較補足
同じ情報をそれぞれの形式で表現すると、可読性の違いがわかりやすい。
YAML
person:
name: John Doe
age: 30
hobbies:
- Reading
- Cycling
- Hiking
JSON
{
"person": {
"name": "John Doe",
"age": 30,
"hobbies": ["Reading", "Cycling", "Hiking"]
}
}
XML
<person>
<name>John Doe</name>
<age>30</age>
<hobbies>
<hobby>Reading</hobby>
<hobby>Cycling</hobby>
<hobby>Hiking</hobby>
</hobbies>
</person>
- 見るとわかるように、XMLよりもYAML・JSONの方が読みやすい。特にYAMLが、カッコなどの記号が少なく、読みやすいことがわかる。
- またYAMLは、後述するコメントやアンカー・エイリアスという、JSONが持たない可読性を高める機能を持っており、他の形式と比べて、可読性の高さは頭ひとつ抜けている。
- このテーマについてより詳しくは、「XML vs. JSON vs. YAML」や「YAML と JSON の違いは何ですか?」などを参照。
YAMLの基本構文
- 本記事では、あらゆる設定ファイルを記述するために必要な、最低限のYAMLの構文のみを扱う。なので、本記事で扱う構文を押さえておくだけでも、ほとんどのケースでは、YAMLの知識不足で困ることはないはず。
- YAMLの基本構文をすっきり理解する上では、次の図が参考になる。以降では、次の図をもとに各要素を解説していく。
サンプル
- 以下はYAMLのサンプル(上:ブロックスタイル、下のコメント:フロースタイル)。
- サンプルだけ見たら理解できる人もいるかもしれないので、最初に載せておきます。
1要素のマップ
name: John
# {name: John}
2要素のマップ
name: John
age: 30
# {name: John, age: 30}
値が1要素のシーケンスのマップ
name: John
children:
- Robert
# {name: John, children: [Robert]}
値が2要素のシーケンスのマップ
name: John
children:
- Robert
- Mary
# {name: John, children: [Robert, Mary]}
要素がマップのシーケンスを値に持つマップ
name: John
children:
- name: Robert
age: 10
- name: Mary
age: 5
# {name: John, children: [{name: Robert, age: 10}, {name: Mary, age: 5}]}
リテラルスタイル(改行を保持)
name: John
motto: |
If you can dream it,
you can do it.
# {name: John, motto: "If you can dream it,\nyou can do it.\n"}
リテラルスタイル(最後の改行を破棄)
name: John
motto: |-
If you can dream it,
you can do it.
# {name: John, motto: "If you can dream it,\nyou can do it."}
折りたたみスタイル(改行を空白に変換、空行を改行に変換)
name: Mary
motto: >
Stay hungry.
Stay foolish.
Steve Jobs
# {name: John, motto: "Stay hungry. Stay foolish.\nSteve Jobs\n"}
折りたたみスタイル(最後の改行を破棄)
name: Mary
motto: >-
Stay hungry.
Stay foolish.
Steve Jobs
# {name: John, motto: "Stay hungry. Stay foolish.\nSteve Jobs"}
以上のサンプルと、前掲の図をすべて理解できるだけの知識を、これから解説していきます。
Kind(データの分類)
- まずは、図の縦軸のKindについて解説。
- YAMLで扱うデータは、単一の値を扱うScalar(スカラ)と、複数の値を扱うCollection(コレクション)に分類される。
- Collectionには、Sequence(シーケンス)とMapping(マップ)が含まれる。
- つまりYAMLには、Scalar/Sequence/Mappingという3つのデータの分類がある。
- この3つの分類のみで、あらゆるプログラミング言語のネイティブデータを表現するのがYAMLの構造。3つの分類だけで、あらゆるプログラミング言語のデータを表現するための仕組みが、裏側に用意されている。
- 以下では、次のYAMLを元に、各分類について解説していく。
# Profile
person:
name: John Doe
age: 30
hobbies:
- Reading
- Cycling
- Hiking
Scalar(スカラ)
- 単一の値を表すデータ型。スカラには、文字列・数値・ブール値・null・日付と時刻などが含まれる。
- 上記のYAMLにおいては、「30」は整数値のスカラ、「John Doe」や「Reading」「Cycling」「Hiking」に加えて、マップのキー(後述)はすべて文字列型のスカラ。
- ちなみに、1行目はコメント。YAMLではJSONとは異なり、コメントを使用することができる。
Mapping(マップ)
- プログラミング言語における連想配列のように、キーと値のペアを表現するデータ型。キーと値は「:」で区切る。
- なので、上記のYAMLは、マップの中にマップがネストされている構造になっている。(キー「person」の値がマップ)
Sequence(シーケンス)
- プログラミング言語における配列やリストのように、複数の要素をまとめるデータ型。「-(dash)」とスペースを組み合わせた記号「- 」を、各要素の先頭に記述する。
- 上記のYAMLにおいては、キー「hobbies」の値は「Reading」「Cycling」「Hiking」という3要素のシーケンス。
Style(表記方法)
- 次に、図の横軸のStyleについて解説。
- それぞれのデータタイプには、ブロック(Block)・フロー(Flow)という、2つのスタイル(表記方法)がある。
- どちらで書いても、YAMLには同じ意味で解釈されるので、可読性が判断基準になる。
ブロックスタイル
まずは、ブロックスタイルについて解説。
ブロックスタイルの例
person:
name: John Doe
age: 30
hobbies:
- Reading
- Cycling
- Hiking
- ブロックスタイルは、カッコなどの記号をなるべく使わずに、インデントで構造を表現する方法。
- 記号を使わないので、可読性を維持することができる。YAMLの可読性を支える記法であり、基本的には、ブロックスタイルで書くのが望ましい。
スカラ
ブロックスタイルのスカラにおいて、Literal(リテラル)とFolded(折りたたみ)という2つのスタイルがある。これらのスタイルによって、非常に長い値のスカラを用いる場合でも、可読性を下げずに表現することが可能になる。
- Literal(リテラル)スタイルは、改行(\n)がそのまま保存されるスタイル。
- 「|」という記号を使う。
- 末尾にも改行がつく。
- 次のサンプルの一番下の行を見ると、改行が「\n」として表現されているのがわかる。
name: John
motto: |
If you can dream it,
you can do it.
# {name: John, motto: "If you can dream it,\nyou can do it.\n"}
- Folded(折りたたみ)スタイルは、改行が空白に変換され、空行が改行に変換されるスタイル。
- 「>」という記号を使う。
- 末尾には改行がつく。
- 次のサンプルの一番下の行を見ると、改行が半角スペースとして表現されているのがわかる。
name: Mary
motto: >
Stay hungry.
Stay foolish.
Steve Jobs
# {name: John, motto: "Stay hungry. Stay foolish.\nSteve Jobs\n"}
- どちらのスタイルでも、「|-」「>-」というように変更すれば、末尾の改行(\n)を破棄することができる。
- 以下のサンプルの一番下の行を見ると、末尾の「\n」が消えていることを確認できる。
name: John
motto: |-
If you can dream it,
you can do it.
# {name: John, motto: "If you can dream it,\nyou can do it."}
name: Mary
motto: >-
Stay hungry.
Stay foolish.
Steve Jobs
# {name: John, motto: "Stay hungry. Stay foolish.\nSteve Jobs"}
コレクション
- 前掲の図を見ると、コレクションにおいては、In-Line(インライン)スタイルとNext Line(ネクストライン)スタイルの2つがある。
- その名の通り、インラインスタイルは1行で書く方法で、ネクストラインスタイルは改行して書く方法。
- In-Lineスタイルを用いるなら、フロースタイルを採用する必要がある。
- より詳しくは、フロースタイルの項目で解説。
フロースタイル
ここからは、もう一つのスタイルである、フロースタイルについて解説。
フロースタイルの例
{name: John, children: [{name: Robert, age: 10}, {name: Mary, age: 5}]}
- フロースタイルは、JSONのように、カッコなどの記号を用いて表現する方法。
- 以下では、コレクションとスカラに分けて、それぞれ解説。
コレクションのフロースタイル(Explicit)
フロースタイルにおいて、コレクションは、
- マップは「{キー1: 値1, キー2: 値2, …}」の形式
- シーケンスは「[値1, 値2, …]」の形式
で表現する。カッコでデータを囲う、明示的な(Explicit)記法。
{name: John, age: 30} # マップ(フロースタイル)
[Robert, Mary] # シーケンス(フロースタイル)
ブロックスタイルとフロースタイルの比較(上記のサンプルを再掲)
name: John
children:
- Robert
- Mary
# {name: John, children: [Robert, Mary]}
Single Pair(マップ)
また、マップのフロースタイルでは、マップがキー・値のペアを1つだけ含む場合に限り、フロースタイルでも{}記号が不要になる。
[
foo: bar
]
# [{foo: bar}]
スカラのフロースタイル
スカラのフロースタイルは、
- double-quoted(ダブルクォーテーション)
- single-quoted(シングルクォーテーション)
- plain (クォーテーションなし)
の3つがある(次のサンプルを参照)。
name: "John" # double-quoted
from: 'New York' # single-quoted
partner: Mary # plain
# {name: "John", from: 'New York', partner: Mary} と同義
double-quoted(ダブルクォーテーション)
- ダブルクォーテーションで囲まれた文字列内では、エスケープシーケンス(例:
\n
での改行、\t
でのタブなど)が解釈される。 - また環境によっては、「$」から始まる値は、環境変数として認識されるなど、一部の文字をエスケープしない。
text: "Stay hungry.\nStay foolish.\n"
# \nは改行の意味になるので
# Stay hungry.
# Stay foolish.
# という意味になる
single-quoted(シングルクォーテーション)
シングルクォーテーションで囲まれた文字列内では、特別な意味を持つ記号は存在せず、すべて文字通りに解釈される。
text: 'Stay hungry.\nStay foolish.\n'
# \nは改行の意味にならないので
# Stay hungry.\nStay foolish.\n
# という意味になる
シングルクォーテーション内で、シングルクォーテーションを使用したい場合は、以下のように「”」を使う。
text: 'It''s OK.'
# It's OK.
plain (クォーテーションなし)
エスケープシーケンスも必要なく、エスケープする文字もない場合は、クォーテーションなしの文字列で十分。最も可読性に優れるため、YAMLでは基本的に、plainスタイルで書くのが良い。
注意点として、次のような文字列は別のデータ型として解釈されるので、単に文字列として使いたい場合には、クォーテーションを使ったエスケープが必要。
- null
- true
- false
※エスケープや環境変数などの用語がわからないという方は、以下の拙著などを参照。
最初の図を再掲
- 次の図の意味がわかるようになったでしょうか?
- もし、わからない部分があれば、上に戻って復習してみてください。
その他の重要構文
コメント
- YAMLには解釈されない、人間のためのテキスト(コメント)を追加することができる。
- これは、JSONにはない機能で、YAMLの可読性の高さに貢献している。
# Comment
アンカーとエイリアス
- YAMLには、アンカーとエイリアスの機能が備わっている。
- これは、特定のデータを別の場所で再利用するために使用される機能で、データの重複を避け、文書の保守性を向上させることができる。
- あとで参照したい値にアンカーを設定して、エイリアスで値を参照する。アンカーは「&名前 値」、エイリアスは「*名前」で使用できる。
favorite_color: &color blue # blueという値にcolorというアンカーを設定
my_color: *color # エイリアスで値(blue)を参照
以下の例では、マップ(「street」「city」などのキーを持つ4要素のもの)にアンカーを設定している。
addresses:
home: &home_address # 以下のマップに、home_addressという値を設定
street: "123 Main St"
city: "Anytown"
state: "Anystate"
zip: "12345"
employees:
- name: John Doe
role: Manager
address: *home_address # エイリアスで値(4要素のマップ)を参照
- name: Jane Smith
role: Developer
address: *home_address # エイリアスで値(4要素のマップ)を参照
上記のサンプルを、アンカーとエイリアスを使わずに書くと次のようになる。
addresses:
home:
street: "123 Main St"
city: "Anytown"
state: "Anystate"
zip: "12345"
employees:
- name: John Doe
role: Manager
address:
street: "123 Main St"
city: "Anytown"
state: "Anystate"
zip: "12345"
- name: Jane Smith
role: Developer
address:
street: "123 Main St"
city: "Anytown"
state: "Anystate"
zip: "12345"
YAMLの練習問題
ここまでの理解を確認するために、以下のYAMLの練習問題に回答してみましょう。これらの問題はすべて、ChatGPTが作成したものを、ひらまつが監修したものです。解答は問題の後に掲載しています。
選択問題
- YAMLファイル内でコメントをどのように記述しますか?
- A:
#
- B:
//
- C:
/* */
- A:
- マップを表すために、YAMLではどの記号を使用しますか?
- A:
=
- B:
:
- C:
-
- A:
- YAMLでシーケンスをどのように記述しますか?
- A: カンマ区切りで直接記述する
- B: 各要素の前に
-
記号をつけて記述する - C:
{}
を使用して記述する
- YAMLで真偽値を表す正しい方法はどれですか?
- A:
true
とfalse
- B:
yes
とno
- C: 両方とも正しい
- A:
- YAMLファイル内で、複数行の文字列を格納するためにはどのように記述しますか?
- A:
|
記号を使用 - B:
>
記号を使用 - C: 両方とも正しい
- A:
- YAMLで使用できるデータ型はどれですか?(複数選択)
- A: 文字列
- B: 数値
- C: 真偽値
- D: 日付と時刻
- YAMLファイルの拡張子として正しいものはどれですか?
- A:
.yml
- B:
.yaml
- C: 両方とも正しい。
- A:
- YAMLでネストされた構造を記述するより一般的な方法として、正しいものはどれですか?
- A: インデントを使用して階層構造を表現する
- B:
[]
を使用して階層構造を表現する - C:
{}
を使用して階層構造を表現する
- YAMLでアンカーを参照するために使用する記号は何ですか?
- A:
*
- B:
&
- C:
#
- A:
- YAMLでマップの値としてリストを使用する方法はどれですか?
- A:
- key: [list, of, values]
- B:
key: - list - of - values
- C:
key: [list, of, values]
- A:
読み取り問題
color: blue
size: large
shape: square
car:
make: Toyota
model: Prius
features:
- hybrid
- automatic transmission
pets:
- name: Bella
type: dog
breed: Labrador
- name: Coco
type: bird
breed: Parrot
employees:
- id: 1
name: John Doe
position: Developer
- id: 2
name: Jane Smith
position: Designer
config:
database:
host: localhost
port: 5432
security:
encryption: AES
key_length: 256
description: |
This is a multi-line string.
YAML is a human-readable data serialization
standard for all programming languages.
description: >-
This is a multi-line string.
YAML is a human-readable data serialization
standard for all programming languages.
teams:
- name: Team A
members:
- Alice
- Bob
- name: Team B
members:
- Carol
- Dave
matrix:
- [1, 2, 3]
- [4, 5, 6]
- [7, 8, 9]
pitching:
- Nolan Ryan
# Following node labeled SP
- &SP Sandy Koufax
wins:
- *SP # Subsequent occurrence
- Pedro Martinez
解答
選択問題
- A
- B
- B
- A
- C
- A, B, C, D
- C
- A
- A
- C
注意点
YAML1.1からYAML1.2に移行する際に、いくつかの仕様の変更が行われました。Web上の情報ではこれらが混在しているため、注意が必要です。
具体的には、以下のようなものが変更点として挙げられます。
- 真偽値として、yes/noなどが使えなくなり、true/false以外は文字列として解釈されるようになった。
- マージキー(<<)の機能が削除された。
- アンカーの文字列に、[]と{}を入れられなくなった。
など。より詳しくは、こちらを参照してください。
また、膨大な記事になってしまったので、ミスがありましたら、ひらまつのX(Twitter)などにご連絡いただけると嬉しいです。