プログラマーのための YAML 入門 (中級編)
初稿:2005-10-10
書いた人:kwatch
はじめに
YAML(YAML Ain’t Markup Language)とは、構造化されたデータを表現するためのフォーマットです。XML と目的は同じですが、XML と比べて「読みやすい」「書きやすい」「わかりやすい」という特徴があります。
また YAML では、データを以下の 3 つで表現します。XML と違って特別な概念が必要ないので、データの操作が非常に簡単です。
- 配列
- ハッシュ
- スカラー(文字列、数値、真偽値、日付など)
前回の初級編では、YAML の書き方を中心に説明し、XML との比較も行いました。今回の中級編では、YAML を扱うライブラリ Syck の機能を紹介します。また Syck を PHP と Python から使うための方法も説明します。
前回からの補足事項
- Syck について
- OCaml のサポートは中止になりました。かわりに、Lua と Cocoa がサポートされました。
- Ruby 1.8.2 付属の Syck はバージョンが 0.45 で、1.8.3 では 0.60 にあがっています。変更点は Syck の ChangeLog をご覧ください。
- データ型について
- 真偽値と解釈されるデータは、”true/false”, “yes/no” のほかに、”on/off” があります。また大文字でも CamelCase(先頭だけが大文字)でも認識されます
- YAMLの仕様にはないですが、Syck では “:foo” のような「コロン+単語」の組み合わせは Ruby の Symbol と解釈されます。
- Syck が解釈するデータ型のパターンは、Syck のソースでは lib/implicit.re で定義されています。独自のデータ型パターンを登録するにはこのファイルを編集して Syck を再コンパイルするしかなく、Ruby から登録することは今のところできません。
- その他
- YAMLはインデントで構造を表現しますが、インデントを操作するにはエディタの矩形(くけい)編集機能を使うと便利です。Emacs や xyzzy をお使いの方は、C-x r t(string-rectangle)や C-x r k(kill-rectangle)を使ってみてください。
目次
Syck について
Syck とは、YAML を扱うためのライブラリです。作者である why the lucky stiff および協力者の尽力により、現在では Ruby、Python、PHP、Cocoa、Lua で使用できます(前回、OCaml もサポートしていると紹介しましたが、今はやめたそうです)。
Syck では、以下のことが行えます。ただし Ruby 以外の言語ではサポートしていない機能のほうが多いです。
- YAML ドキュメントから Ruby オブジェクトへの変換
- Ruby オブジェクトを YAML 形式の文字列に変換
- YAML ファイルをデータベースとみなしての、トランザクション処理
- YAML ドキュメントからツリーへの変換およびツリーの操作
why the lucky stiff は、はじめは YAML4R という pure Ruby な YAML ライブラリを作成しました。そのあと、高速化と他言語への移植を考えて、C 言語で書き直しました。それが Syck です。現在、YAML4R は Syck の一部となっています。
Ruby では 1.8 から Syck が標準ライブラリに含まれており、「requre ‘yaml’」とするだけで、Syck が使用できます。
Ruby 1.6 には含まれていませんので、ダウンロードページからダウンロードし、インストールしてください。
Syck の機能
YAML ドキュメントから Ruby オブジェクトへの変換
YAML ドキュメントを Ruby オブジェクトに変換するには、YAML.load() または YAML.load_file() を使います。
- YAML.load(input)
- YAML ドキュメントをオブジェクトに変換します。引数には String または IO オブジェクト(File オブジェクトを含む)を指定します。
- YAML.load_file(filename)
- YAML ファイルを読み込み、Ruby オブジェクトに変換します。
example01.rb : YAML ドキュメントをオブジェクトに変換する
実行結果:
Ruby オブジェクトから YAML 形式の文字列への変換
Ruby オブジェクトを YAML 形式の文字列に変換するには、Object#to_yaml() または YAML.dump() を使います。
- Object#to_yaml()
- 任意の Ruby オブジェクトを YAML 形式の文字列に変換します。「require ‘yaml’」を実行すると追加されます。
- YAML.dump(obj)
- 引数で指定した Ruby オブジェクトを YAML 形式の文字列に変換します。Object#to_yaml() と同じであり、内部で Object#to_yaml() を呼び出しています。
example02.rb : Ruby オブジェクトを YAML 形式に変換する
実行結果:
本稿では、YAML を「構造化されたデータを表現するためのフォーマット」と紹介していますが、YAML は本来「データをシリアライズするためのフォーマット」です(YAML がわざわざ「マークアップ言語ではない(YAML Ain’t Markup Language)」と強調しているのは、このためです)。つまり Ruby オブジェクトを文字列へ変換することは、YAML の本来の使い方といえます。
オブジェクトを YAML 形式の文字列に変換するときには、以下の点に注意してください。
- ドキュメントの開始を表す “—” は、Ruby 1.8.2 では出力され、1.8.3 では出力されません。
- ハッシュにおけるキーの順番は不定です。読み込んだときとは違う順番で出力されます。
- 読み込んだときがフロースタイルでも、出力はブロックスタイルになります。
- Proc オブジェクトや IO オブジェクトは変換できません。
- オブジェクトは最初に現れた時点で出力されます。アンカーを使っている場合、読み込んだときとは別の位置にデータが出力されることがあります。
- 読み込み時のアンカー名は保存されません。出力するときのアンカー名は読み込み時とは別になります。
データバインディング
YAML.dump() では、ユーザ定義クラスの Ruby オブジェクトも YAML 形式に変換できます。
example03.rb : ユーザ定義クラスの Ruby オブジェクトを YAML 形式に変換
実行結果:
この出力ファイルから Ruby オブジェクトを復元することもできます(これはシリアライズとは逆の、デシリアライズになります)。上の出力を「data.yaml」というファイル名で保存し、次のプログラムを実行してみてください。
example04.rb : YAML ドキュメントをユーザ定義クラスのオブジェクトに変換
実行結果:
YAML や XML のようなデータをユーザ定義のオブジェクトに変換する機能は、XML の世界ではデータバインディングと呼ばれます。XML ではデータバインディング用のスキーマやツールを別途用意しなくてはいけませんが、YAML ではその必要はありません。ただし、ドキュメントの中にデータ型を明示しなければなりません。
ストリームと複数の YAML ドキュメント
YAML では、ひとつのファイルに複数の YAML ドキュメントを格納することができます。個々の YAML ドキュメントは「—」で区切ります。複数の YAML ドキュメントを含むデータを、YAML ではストリームといいます。
データをストリームとして読み込む、つまり複数のYAML ドキュメントを読み込むには、YAML.load_documents() または YAML.load_stream() を使います。引数 input には文字列または IO オブジェクト(File オブジェクトを含む)が指定できます。
- YAML.load_documents(input, &block)
- YAML ドキュメントをひとつずつ読み込み、ブロックを実行します。
- YAML.load_stream(input)
- YAML ドキュメントをすべて読み込み、YAML::Stream オブジェクトにまとめて返します。
example05.rb : ストリームから複数の YAML ドキュメントを読み込む
実行結果:
ストリームを使う利点は次の2つです。
- データ全体を読み込まなくても、読み込んだはしから YAML ドキュメントを取得できます。遅いネットワークから読み出す場合などに効果があります。
- YAML ドキュメントをひとつずつ読み込むので、すべてのデータを一度に読み込むのと比べて、メモリ消費が少なくて済みます。
例えば、次の例ではデータ全体を読み込まないと YAML ドキュメントを得ることができません。また一度に多くのメモリを消費します。
これに対し、次の例では各データを個別の YAML ドキュメントとしています。そのため、すべてを読み込まなくても YAML ドキュメントを得ることができ、かつメモリも何回かに分けて少しずつ消費するようになります。
またストリームを使って、ひとつのファイルに複数の YAML ドキュメントを書き込むには、YAML.dump_stream() または YAML::Stream#emit() を使います。
- YAML.dump_stream(*docs)
- 引数で渡した各ドキュメントを「—」でつなげた文字列を返します。
- YAML::Stream#emit()
- ストリームに含まれる各ドキュメントを「—」でつなげた文字列を返します。
example06.rb : ストリームに複数の YAML ドキュメントを出力する
実行結果:
ツリーと YPath
Syck には、YAML ドキュメントをパースしツリー構造のデータに変換する機能があります。そのためには、YAML.parse() または YAML.parse_file() を使います。これらはツリーを返します(具体的には YAML::Syck::Node オブジェクトを返します)。
- YAML.parse(input)
- 文字列または IO オブジェクトを読み込み、ツリーを返します。
- YAML.parse_file(filename)
- YAML ファイルを読み込み、ツリーを返します。
複数の YAML ドキュメントを読み込むには、YAML.parse_documents() を使います。YAML.parse_stream() は定義されていないようです。
- YAML.parse_documents(input, &block)
- YAML ドキュメントをひとつずつ読み込みながら、ブロックを実行します。
またツリーに対して、パスを指定してノードにアクセスすることができます。この機能は YPath といい、XML における XPath に相当します。パスにはメタキャラクタが指定できます。
- YAML::Syck::Node#search(path)
- ツリーに対してパスを検索します。マッチしたパスをすべて含んだ配列が返されます。
- YAML::Syck::Node#select(path)
- パスにマッチしたノードを取り出します。1.8.3 ではマッチしたノードをすべて含んだ配列が返され、1.8.2 ではその配列を表すノードが返されます。
- YAML::Syck::Node#transform()
- ツリーをデータオブジェクトに変換します。
example07.rb : ツリーと YPath
実行結果(Ruby 1.8.3):
実行結果(Ruby 1.8.2):
なお YPath はまだ仕様が決まっているわけではないので、将来変更される可能性があります。また XPath ほど高度な条件は今のところサポートしていません。
トランザクション
Syck では、トランザクションを使ってデータをファイルに保存することができます。トランザクション機能では、エラーがなかった場合(=例外が発生しなかった場合)にのみデータをファイルに書き込みます。エラーがあった場合(=例外が発生した場合)はファイルへの書き込みを行いません。
トランザクション機能を使うには、YAML::Store クラスを使います。
- YAML::Store.new(filename)
- ファイル名を指定して YAML::Store オブジェクトを作成します。
YAML::Store クラスは PStore クラスを継承しています。実際のプログラムでは、PStore クラスから継承した以下のようなメソッドを使います。
- PStore#transaction(&block)
- ブロックを実行します。例外が発生しなければファイルに書き込まれ、発生すれば書き込まれません。
- PStore#commit()
- トランザクション処理をコミットします。実行されるとデータがファイルに書き込みます。
- PStore#abort()
- トランザクション処理を破棄します。実行されるとデータがもとに戻されます。
- PStore#[key]
- データを読み出します。
- PStore#[key]=value
- データを書き込みます。
次の例は、エラーがない場合です。
example09.rb : トランザクション処理
このスクリプトを何度か実行すると、データが保存されていることがわかります。
実行例:データが保存されている
次の例は、トランザクション中に例外を発生させた場合の例です。
example10.rb : トランザクション中に例外が発生した場合
このスクリプトを何度か実行すると、データを格納したはずのにファイルには保存されていないことがわかります。これは、トランザクション中に例外が発生したためです。
実行例:データが保存されていない
Kernel.y() メソッド
「require ‘yaml’」を実行すると、Kernel.y() メソッドが追加されます。これはちょうと Kernel.p() と同じで、デバッグ用に使います。Kernel.p() が Object#inspect() を呼び出すのに対し、Kernel.y() は Object#to_yaml() を呼び出します。
example12.rb : オブジェクトを YAML 形式に変換して表示
実行結果:
実行結果における先頭の「—」は、Ruby 1.8.2 では出力されますが、1.8.3 だと出力されません。
Syck の他言語用バインディング
Syck には標準で Ruby、PHP、Python、Cocoa 用のバインディングが付属します。CVS には Lua 用のバインディングもあります(OCaml のサポートはあきらめたそうです)。ここでは、PHP と Python のバインディングを紹介します。少し Ruby から離れますが、ご了承ください。
なお Syck のダウンロードは RubyForge から行ってください。CVSでのバージョンは 0.60 ですが、一般向けにダウンロード可能になっているのは原稿執筆時(2005年9月)で 0.55 ですので、以降では 0.55 を使って説明します。
PHP 用バインディング
PHPユーザのみなさん、こんにちは。ここでは PHP 用バインディングについて説明します。
Syck の PHP 用バインディングは、Syck を PHP で使えるようにするための拡張モジュールです。これをインストールすると、PHP から Syck が使えるようになります。
ただし Syck の全機能が使えるわけではなく、Syck 0.55 でサポートされているのは YAML ドキュメントの読み込みを行う関数「syck_load()」だけです。
今回は次のような環境で試しました。Fedora Core や Debian をお使いの方は、Apache と PHP の開発用パッケージもインストールするのを忘れないでください。
- Debian GNU/Linux Sarge i686 with coLinux(Linux 2.4.26-co-0.6.1)
- Apache 2.0.54-5 prefork(「apt-get install apache2 apache2-mpm-prefork apache2-prefork-dev」を実行)
- PHP4.4.0 ソースからコンパイル
なお Fedora Core 4 には専用の rpm パッケージがあるようです。Fedora Core 4 をお使いの方は、こちらをお試してみるのもいいでしょう。
Syck のコンパイルとインストールの手順は次の通りです。README に記載の手順ではうまくいきませんので、下記の手順に従ってください。
make install を実行すると、拡張モジュールが例えば /usr/local/lib/php/extensions/no-debug-non-zts-20020429/syck.so にコピーされます(ディレクトリは環境によって異なります)。
このあと、PHP の設定ファイル「php.ini」の extension_dir に拡張モジュールが置かれているディレクトリを指定してください。なお拡張モジュールをシステムにインストールできない場合でも、拡張モジュールのパスを明示的に指定すれば使用できます。
/usr/local/lib/php.ini :
サンプルプログラムと実行例は次の通りです。
サンプルプログラム(syck-test.php):
実行例 :
Python バインディング
Python ユーザのみなさん、こんにちは。ここでは Syck の Python バインディングについて説明します。
Python バインディングは、Syck を Python から使えるようにする拡張モジュールです。これをインストールすると、Python から Syck が使えるようになります。
ただし Syck 標準の Python バインディングだと YAML ドキュメントの読み込みしかできません。最近、読み込みと書き込みが行える「PySyck」という新しいバインディングが発表されましたので、こちらを使って見ましょう。
ダウンロード: http://xitology.org/pysyck/PySyck-0.55.1.tar.gz
インストール:
python setup.py install を実行すると、/usr/lib/python2.3/site-packages/ 以下に Syck がインストールされます(Debian/GNU Linux の場合)。
サンプルプログラムと実行例は次の通りです。
サンプルプログラム(syck-test.py):
実行例 :
Syck 以外の YAML パーサ
ついでに、Syck 以外の YAML パーサも紹介してみます。どれも Ruby 以外の言語による実装です。どんどん Ruby から離れていくんですけど、るびまでこんな記事書いていいんでしょうか。編集長ごめんなさい。
PyYaml(Python)
PyYaml は、Python による実装です。かなり本格的に作られており、Python で使うなら Syck より高機能です。
インストール:
サンプルプログラム(pyyaml-test.py):
実行例 :
Spyc(PHP)
Spyc は、PHP による実装です。YAML ファイルの読み込みと書き込みができます。
ダウンロード:spyc-0.1.1.tar.gz
インストール :
サンプルプログラム(spyc-test.php):
実行結果 :
よくみたら、TRUE が 1 になっています。たぶんバグですが、大目にみてあげてください。
なお Spyc を PHP5 で使う場合は、『SpycをPHP5で使う』(BMediaNode) もご覧ください。
A YAML parser written in Java(Java)
A YAML parser written in Java は、Java による実装です。
他の実装と違い、SAX のようなイベント駆動型のパーサです。また型の判別は行っていません。現在は開発が中止されています。
YAML JavaScript(JavaScript)
YAML JavaScript は、JavaScriptによる実装です。
2003年2月以降、更新されていないようです。
dyayaml(D 言語)
dyayaml は、D 言語で書かれた実装です。D 言語、Delphi、Ada で使えるそうです。詳しくは Web ページをご覧ください。
YAML 用語
ここでもう一度、YAMLの用語について説明しておきます。詳しくは YAML の仕様書をご覧ください。
- シーケンス(Sequence)
- 複数のデータを保持するためのコンテナ。Ruby では Array に相当。
- マッピング(Mapping)
- キーと値とを結びつけて保持するためのコンテナ。Ruby では Hash に相当。
- コンテナ(Container)
- 他のデータを格納するためのデータ。シーケンスとマッピングが代表的。他に、値の重複を許さない「セット(Set)」、要素が 2 つのシーケンスである「タプル(Tuple)」などがある。
- スカラー(Scalar)
- コンテナではないデータ。文字列、数値、真偽値、日付、タイムスタンプなどが代表的。
- ストリーム(Stream)
- 複数の YAML ドキュメントが格納されたデータ。
- ドメイン(Domain)
- データ型の名前空間。データ型は必ず何らかのドメインに属する。通常は気にする必要はない。
終わりに
本稿では、YAML という「仕様」を扱うための「実装」である Syck について説明しました。Syck を使うと、YAML ドキュメントをオブジェクトに変換したり、その逆にオブジェクトを YAML ドキュメントに変換できます。また YAML ドキュメントをツリーに変換して操作することもできます。
Syck の機能をまとめると次のようになります。
クラスとメソッド |
説明 |
Object#to_yaml |
オブジェクトを YAML 形式の文字列に変換する |
YAML.dump(object) |
オブジェクトを YAML 形式の文字列に変換する |
YAML.load(input) |
YAML ドキュメントをひとつだけ読み込んでオブジェクトに変換する |
YAML.load_file(filename) |
ファイル名を指定して YAML ドキュメントを読み込み、変換する |
YAML.load_documents(input, &block) |
YAML ドキュメントをひとつずつ読み込み、ブロックを実行する |
YAML.load_stream(input) |
YAML ドキュメントを複数読み込んでオブジェクトに変換する |
YAML.parse(input) |
YAML ドキュメントをひとつだけ読み込んでツリーに変換する |
YAML.parse_stream(input) |
YAML ドキュメントを複数読み込んでツリーに変換する |
YAML::Syck::Node#search(path) |
パスを検索し、マッチしたパスの文字列を配列で返す。 |
YAML::Syck::Node#select(path) |
パスを検索し、特定のノードを取り出す |
YAML::Syck::Node#transform() |
ノードをデータに変換する。 |
YAML::Store.new(filename) |
ファイル名を指定して Store オブジェクトを作成する |
YAML::Store#transaction(block) |
ブロックを実行し、例外がなければ YAML ドキュメントをファイルに保存する |
また、Syck の PHP バインディングと Python バインディングについて説明しました。これらをインストールすると、PHP や Python から Syck を利用することができます。ただし、機能は限定されます。
次回は実践編と題して、YAML を使ったアプリケーションを作成します。
参考文献
- Syck ホームページ
- Syck のホームページです。サポートする機能・しない機能・作業中の機能を色分けで表示しています。
- YAML4R マニュアル
- YAML4R のマニュアルですが、Syck でも参考になります。また Windows ユーザ向けの chm ファイルもあります。
- RedHanded
- Syck の作者である why the lucky stiff が参加している blog です。
著者について
名前:kwatch。三流プログラマー。猛虎優勝はうれしいはずなのに、強い阪神になぜか違和感を感じる今日この頃。「ダメ虎」という言葉に愛着を感じるのは自分がダメ人間だからか。最近のお気に入りは「萌えたコピペ」。
プログラマーのための YAML 入門 連載一覧