【速習】YAML1.2の概要と基本構文【練習問題付き】

Flutter

こんにちは、教育系エンジニアのひらまつ(@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には以下のようなゴールが設定されている(数字が小さいほど優先度が高い)。

  1. YAMLは、人間にとって簡単に読めるべきである。
  2. YAMLデータは、プログラミング言語間で移植可能であるべきである。
  3. YAMLは、動的言語のネイティブデータ構造に合致すべきである。
  4. YAMLは、汎用ツールをサポートするための一貫したモデルを持つべである。
  5. YAMLは、ワンパス処理をサポートすべきである。
  6. YAMLは、表現力豊かで、拡張可能であるべきである。
  7. 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の基本構文をすっきり理解する上では、次の図が参考になる。以降では、次の図をもとに各要素を解説していく。
Kind/Style Combinations
引用:https://yaml.org/spec/1.2.2/#3231-node-styles

サンプル

  • 以下は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

※エスケープや環境変数などの用語がわからないという方は、以下の拙著などを参照。

最初の図を再掲

  • 次の図の意味がわかるようになったでしょうか?
  • もし、わからない部分があれば、上に戻って復習してみてください。
引用:https://yaml.org/spec/1.2.2/#3231-node-styles

その他の重要構文

コメント

  • 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が作成したものを、ひらまつが監修したものです。解答は問題の後に掲載しています。

選択問題

  1. YAMLファイル内でコメントをどのように記述しますか?
    • A: #
    • B: //
    • C: /* */
  2. マップを表すために、YAMLではどの記号を使用しますか?
    • A: =
    • B: :
    • C: -
  3. YAMLでシーケンスをどのように記述しますか?
    • A: カンマ区切りで直接記述する
    • B: 各要素の前に-記号をつけて記述する
    • C: {}を使用して記述する
  4. YAMLで真偽値を表す正しい方法はどれですか?
    • A: truefalse
    • B: yesno
    • C: 両方とも正しい
  5. YAMLファイル内で、複数行の文字列を格納するためにはどのように記述しますか?
    • A: | 記号を使用
    • B: > 記号を使用
    • C: 両方とも正しい
  6. YAMLで使用できるデータ型はどれですか?(複数選択)
    • A: 文字列
    • B: 数値
    • C: 真偽値
    • D: 日付と時刻
  7. YAMLファイルの拡張子として正しいものはどれですか?
    • A: .yml
    • B: .yaml
    • C: 両方とも正しい。
  8. YAMLでネストされた構造を記述するより一般的な方法として、正しいものはどれですか?
    • A: インデントを使用して階層構造を表現する
    • B: []を使用して階層構造を表現する
    • C: {}を使用して階層構造を表現する
  9. YAMLでアンカーを参照するために使用する記号は何ですか?
    • A: *
    • B: &
    • C: #
  10. YAMLでマップの値としてリストを使用する方法はどれですか?
    • A: - key: [list, of, values]
    • B: key: - list - of - values
    • C: key: [list, of, values]

読み取り問題

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

解答

選択問題

  1. A
  2. B
  3. B
  4. A
  5. C
  6. A, B, C, D
  7. C
  8. A
  9. A
  10. C

注意点

YAML1.1からYAML1.2に移行する際に、いくつかの仕様の変更が行われました。Web上の情報ではこれらが混在しているため、注意が必要です。

具体的には、以下のようなものが変更点として挙げられます。

  • 真偽値として、yes/noなどが使えなくなり、true/false以外は文字列として解釈されるようになった。
  • マージキー(<<)の機能が削除された。
  • アンカーの文字列に、[]と{}を入れられなくなった。

など。より詳しくは、こちらを参照してください。

また、膨大な記事になってしまったので、ミスがありましたら、ひらまつのX(Twitter)などにご連絡いただけると嬉しいです。

参考文献

YAML Ain’t Markup Language (YAML™) revision 1.2.2
YAML Ain’t Markup Language (YAML™) Version 1.2
YAML Ain’t Markup Language (YAML™) revision 1.2.2
タイトルとURLをコピーしました