はじめに
YAML(YAML Ain't Markup Language)とは,データを構造化して表現するためのフォーマットです。目的はXMLと似ていますが,インデントを主体とした記法のため,XMLより読みやすく,書きやすく,わかりやすくなっています。
またデータシリアライゼーション(注1)に使えるように設計されているため,任意のデータ構造が表現できるだけの記述力を持っています。これは,基本的に木構造しか記述できないXMLと比べて,YAMLの大きな利点です。YAMLの文法については,WEB+DB PRESS Vol.43「最新[データ交換フォーマット]攻略ガイド JSON/YAML実践入門」の第3章「YAML実践リファレンス」に解説がありますので,併せて参照してください。
本特集では,YAMLをさまざまなプログラミング言語で利用するためのYAML用ライブラリの使い方を,各言語ごとに紹介していきます。その際,使い方としては,YAML文字列からデータへの変換だけでなく,データをYAML文字列に変換する方法も紹介していきます。これは,「データ交換」の観点で,データをYAML文字列に変換できるとたいへん便利だからです。
- 注1)
- データをバイト列に変換すること。
YAMLライブラリの内部処理について
ここで,YAMLライブラリが行う内部処理の流れについて説明します。これを知っておくと,各ライブラリの理解が容易になります。YAMLライブラリが行う処理は,いくつかのステージに分かれています(図1)(注2)。
- parse
- YAML文字列を構文解析し,イベントの列に変換します。たとえば「{10, 20}」というYAML文字列は,「StartSequence, ScalarData, ScalarData, EndSequence」というイベントの列に変換されます。
- compose
- イベントの列をノードに変換します(注3)。ノードは3種類あり,シーケンス,マッピング,スカラーを表します。
- construct
- ノードをデータに変換します。たとえばスカラーを表すノードは文字列や数値に変換されます。
- represent
- constructの逆で,データをノードに変換します。
- serialize
- composeの逆で,ノードをイベントの列に変換します。
- present(emit)(注4)
- parseの逆で,イベントの列を文字列に変換します。
またparse,compose,constructからなる一連の処理を「load」といい,represent,serialize,present(emit)からなる一連の処理を「dump」といいます。
これらの詳細については,YAML仕様書『Chaper 3. Processing YAML Information』を参照してください。
イベントやノードについて調べる方法は,各ライブラリによって違います。リスト1は,Python用のライブラリであるPyYAMLを使ったサンプルスクリプトです。またリスト2はその実行例です。これを使うと,どんなイベントやノードに変換されるか調べることができます。
リスト1 yaml-internal.py: PyYAMLを使ってイベントやノードを調べる
# -*- coding: utf-8 -*-
## 使い方: python yaml-internal.py [file.yaml]
import sys
import yaml
## ファイルまたは標準入力からYAML文字列を読み込む
filename = len(sys.argv) > 1 and sys.argv[1] or None
input = (filename and open(filename) or sys.stdin).read()
input = input.encode('utf8') ## 文字コードを変換する
## YAML文字列をイベントの列に変換する
print "===== events ====="
for event in yaml.parse(input):
print event
## YAML文字列からノードを生成する
print "===== node graph ====="
node = yaml.compose(input)
print node
リスト2 yaml-internal.pyの実行例(見やすいように整形済み)
$ cat example.yaml
- abc
- 123
- {x: 10, y: 20}
$ python yaml-internal.py example.yaml
===== events =====
StreamStartEvent()
DocumentStartEvent()
SequenceStartEvent(anchor=None, tag=None, implicit=True)
ScalarEvent(anchor=None, tag=None, implicit=(True, False), value=u'abc')
ScalarEvent(anchor=None, tag=None, implicit=(True, False), value=u'123')
MappingStartEvent(anchor=None, tag=None, implicit=True)
ScalarEvent(anchor=None, tag=None, implicit=(True, False), value=u'x')
ScalarEvent(anchor=None, tag=None, implicit=(True, False), value=u'10')
ScalarEvent(anchor=None, tag=None, implicit=(True, False), value=u'y')
ScalarEvent(anchor=None, tag=None, implicit=(True, False), value=u'20')
MappingEndEvent()
SequenceEndEvent()
DocumentEndEvent()
StreamEndEvent()
===== node graph =====
SequenceNode(tag=u'tag:yaml.org,2002:seq', value=[
ScalarNode(tag=u'tag:yaml.org,2002:str', value=u'abc'),
ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'123'),
MappingNode(tag=u'tag:yaml.org,2002:map', value=[
( ScalarNode(tag=u'tag:yaml.org,2002:str', value=u'x'),
ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'10')
),
( ScalarNode(tag=u'tag:yaml.org,2002:str', value=u'y'),
ScalarNode(tag=u'tag:yaml.org,2002:int', value=u'20')
)
])
])
- 注2)
- すべてのライブラリがこの流れのとおりに処理を行っているわけではありません。
- 注3)
- 正確には,ノードからなる有向グラフに変換します。以降でも,単に「ノード」だけで「ノードからなる有向グラフ」を表すことにします。
- 注4)
- YAMLの仕様書では「present」という用語が使われていますが,実際のライブラリではかわりに「emit」という用語が使われています。

