言語別 YAML用ライブラリ徹底解説

第1回 YAMLライブラリのしくみ

この記事を読むのに必要な時間:およそ 3 分

はじめに

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)。

図1 YAMLライブラリが行う処理の流れ

図1 YAMLライブラリが行う処理の流れ

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」という用語が使われています。

著者プロフィール

桑田誠(くわたまこと)

プログラマー。Javaに対するLL,XMLに対するYAMLなど,複雑なことをシンプルにするような技術に興味をもつ。

URLhttp://www.kuwata-lab.com/

コメント

コメントの記入