筆者の想像では、読者が最初に思い浮かべる質問は、「なぜYAML という名前なのだろう?」というものに違いありません。気取って、"Yet Another XXX (もう1つのXXX)" を意味する "YA*" という頭字語を採用してきたツールはたくさんあります。オープン・ソースのウィットの世界において、YAMLは、その暗黙の頭字語を避け、代わりに再帰的な "YAML Ain't Markup Language (YAMLはマークアップ言語ではない)" に定着しています。しかし、これにはいくらかの意味があります。つまり、YAMLはマークアップ言語が行うことを行いますが、マークアップ を何も必要としないのです。
YAMLは、汎用性の点でXMLに劣るものの、XMLよりずっと読みやすく、編集、修正、および作成がずっと容易です。つまり、XMLで表現できるものなら何でも、YAMLで (ほとんどの場合、よりコンパクトに) 表現できるということです。名前空間は、特別な弁解が必要な1つの事例となっています。名前空間をYAMLに「ボルトで留める」ことはできますが、現在のところ、名前空間は仕様に含まれていません。
このコラムで筆者が頻繁に取り上げてきたXMLへの批判の1つは (これを強調するのは決して筆者一人ではありません)、XMLはフォーカスが十分に絞られていないということです。古典的かつ委員会主導の巨獣であるXMLは、文書フォーマットであろうとし、データ・フォーマットであろうとし、メッセージ・パケット・フォーマットであろうとし、セキュアなRPCチャネルであろうとし (SOAP)、またオブジェクト・データベースであろうとしています。その上、XMLは、アクセスおよび操作のスタイルごとにAPIを上乗せしています。つまり、DOM、SAX、XSLT、XPATH、JDOM、およびより一般性の低いインターフェース層が何ダースもあります (筆者自身も、gnosis.xml.pickle、gnosis.xml.objectify、およびgnosis.xml.validity パッケージでいくらか貢献してきました)。注目すべきこととして、XMLは上記の事柄をすべて行います。しかし、がっかりさせられることに、XMLはこのうちどれも特別上手に行うわけではありません。
YAMLのフォーカスは、より狭い範囲、つまり、動的なプログラム言語 (Perl、Python、Ruby、および程度は限られるもののJavaプログラミングなど) に見られるデータ構造やデータ型をきれいに表現することに絞られています。現時点で、上記の言語用のバインディング/ライブラリーが存在しています。他の多くの言語のデータ型もYAMLと仲良くできる でしょうが、ライブラリーはまだ作成されていません。そうした言語としては、Lisp/Scheme、Rebol、Smalltalk、xBase、およびAWKがあります。動的な度合いの低い言語は、YAMLとそれほど良く調和しないかもしれません。YAMLの構文は、Perlのコンテキストに応じた型付け、Pythonの字下げ構造、およびMIME標準のいくつかの規則を組み合わせたものです。Pythonがしばしば実行可能な疑似コード として称賛されるのとほぼ同様、YAMLの明確な構文は、クラスやワークグループに対してデータ構造を形式ばらないで説明するのに使用するであろうものに非常に近いものとなっています。
さまざまなフォーマットのコードを見てみれば、YAMLを使用したいと思わせる理由が簡単に分かります。今回の記事では、データの保管と伝送という要件を持つ、小さなアプリケーションを作成しているものと想定します。筆者のお気に入りのプロジェクトは、この記事の執筆中に開催された "Brains in Bahrain" チェス・トーナメントからインスピレーションを受けたものです。このトーナメントは、FISAのチェス世界チャンピオンと、最高ランクのコンピューター・プレーヤーの間で争われるものです (データの詳細は話半分に受け取ってください)。チェス・クラブの活動をトラッキングするプログラムを作成する場合、以下のPerlコードによって記述されるデータ構造を使用するかもしれません。
リスト1. Perlで記述したチェス・クラブのデータ構造
$players = {
'Vladimir Kramnik' => {'status'=>'GM', 'rating'=>'2700'},
'Deep Fritz' => {'status'=>'Computer','rating'=>'2700'},
'David Mertz' => {'status'=>'Amateur', 'rating'=>'1400'},
};
$club = {
'_players' => $players,
'matches' => [
{'Date' => '2002-10-04',
'White' => $players->{'Deep Fritz'},
'Black' => $players->{'Vladimir Kramnik'},
'Result' => 'Draw' },
{'Date' => '2002-10-06',
'White' => $players->{'Vladimir Kramnik'},
'Black' => $players->{'Deep Fritz'},
'Result' => 'White' }
]
};
|
Pythonは、上記のPerlのコードに似ています。
リスト2. Pythonで記述したチェス・クラブのデータ構造
ts = yaml.timestamp# or mx.DateTime or othr date class
players = {
'Vladimir Kramnik': {'status':'GM','rating':2700},
'Deep Fritz': {'status':'Computer','rating':2700},
'David Mertz': {'status':'Amateur','rating':1400},
}
matches = [
{'Date': ts('2002-10-04'),
'White': players['Deep Fritz'],
'Black': players['Vladimir Kramnik'],
'Result':'Draw' },
{'Date': ts('2002-10-06'),
'White': players['Vladimir Kramnik'],
'Black': players['Deep Fritz'],
'Result':'White' }
]
club = {'_players':players,'matches':matches}
|
他の動的なプログラム言語でも、同様のデータ構造の記述を使用することでしょう。基本的に言って、これはいくつかの再帰的なレベルの辞書とリストの両方が含まれた、トップレベルの辞書/マッピング/ハッシュです。これはまた、ネストされた要素がお互いを参照することも許可しています。
チェス・クラブを管理するアプリケーションであるなら、おそらく、追加の対局を記録すること、プレーヤーをクラブに追加/削除すること、あるいは対局に基づいてレーティングを更新することなどのタスクを実行できることでしょう。加えて、そのようなアプリケーションは、データのスナップショットを記録 するだけでなく、同じデータ・モデルを処理する他のアプリケーション (他の言語による) とデータを共有 することも望むかもしれません。さらに、アプリケーションの外で、手作業でデータを簡単に修正できれば望ましいことでしょう。データ指向のアプリケーションを開発したことのある人なら、あるいは組織の記録を保守したことのある人なら誰でも、基礎となるデータ構造の「中身をつつく」ことができれば便利だということを知っています。
おそらく読者は、筆者が議論の下準備を終えたことにすでにお気付きでしょう。ここでYAMLが正しい解決策として急降下するのです。しかし、筆者は設定が不当なものとは思いません。データを構造化した方法は、データがそれ自身を提示するときにしばしば用いる方法と、少なくとも大体において、良く似ています。上記のマップ、リスト、参照、およびデータ型は、それぞれが最も自然に見えるような場所で正しく用いるようにしました。また、根底となる問題も、論点を示すのに十分な複雑さでありながら、1つの記事に収まる程度に簡単なものとして選択したものに過ぎません。
すべてのチェス・クラブ・アプリケーションに特定のプログラム言語を (ことによると特定のバージョンも) 要求するつもりなら、大部分の言語には直列化の優れた機能が、組み込みライブラリーまたは共通ライブラリーの形で備わっています。
- Pythonには
cPickle、gnosis.xml.pickle、およびpprintがあります - Perlには
Data::Dumper、Data::Denter、およびData:DumpXMLがあります - Rubyには
MarshalとXmlSerializationがあります - Java言語には
java.io.Serializable、org.apache.xml.serialize.XMLSerializer、および他のさまざまなライブラリーがあります
名前が示しているとおり、上記のライブラリーの一部はXMLを作成します。とは言え、XMLが言語同士の間で容易に転送可能であるとはとても言えません。
それに加えて、このチェス・クラブのデータを表現するためにXMLを使用することには、いくつかの一般的なセマンティクス上の問題があります。XMLでは、要素の属性については順序なしのマッピングの概念があるものの、ネストされた要素については厳密な順序付けを行います。当然、特定のアプリケーションは順序付けの情報の一部を無視 できますが、XMLの情報モデル は、(時には見せかけだけの) 順序付けを行うことの重要性を常に主張しています。たとえば、対局は特定の順序 (日付順) に収まると考えられますが、プレーヤーは本質的に順序付けの対象ではありません。(もちろん、レーティングや入会日付などの順序を押し付ける ことはできるかもしれません。)問題は、すべてのアプリケーションについて、押し付けられた順序付けが見せかけだけのものならいつでもこれを除去 し、順序付けが重要な場合はこれを保持 するためのカスタム・プログラミングが必要になるということです。
XML-RPC、SOAP、gnosis.xml.pickle、およびさまざまな言語のXMLシリアライザー・ライブラリーでは、マッピングを表現するために汎用的なアプローチを採用しています。上記のすべては、基本原則として、順序なしの対を表すには<key> および<val> (または類似のタグ) を (半ば冗長なものとして) 使用し、順序付けされた項目を表すには別のコンテナー要素を使用します。この原則では、XML情報モデルの一部を除去するために、いくつかの層が追加されます。
リスト3. 順序付きおよび順序なし集合のXML-RPCモデル
>>> import xmlrpclib
>>> print xmlrpclib.dumps(({'this':'that',
... 'spam':('eggs','toast')},))
<params>
<param>
<value><struct>
<member>
<name>this</name>
<value><string>that</string></value>
</member>
<member>
<name>spam</name>
<value><array><data>
<value><string>eggs</string></value>
<value><string>toast</string></value>
</data></array></value>
</member>
</struct></value>
</param>
</params>
|
XML-RPCの場合はさらにいくつかの作為が施されていますが (オブジェクト全体を1項目のタプルにラップしなければならないことなど)、こうした事柄は小さな問題に過ぎません。「ネイティブ」のデータ・モデルとXMLデータ・モデルの適合関係がぎこちないことは、ここで言及するあらゆるXML直列化フォーマットについても、同様に明白であると言えます。
このチェス・クラブのデータをXMLで表現することに伴って生じる問題は、少なくとも2つあります。最初の、そしてより単純な問題は、理論的に言って、最良のXML表現とは一体どのようなものかということです。そのために、試みに次のようなものを最良のXMLとして提案したいと思います。
リスト4. チェス・クラブ・データの最適のXML記述
<?xml version="1.0"?>
<club>
<players>
<player id="kramnik"
name="Vladimir Kramnik"
rating="2700"
status="GM" />
<player id="fritz"
name="Deep Fritz"
rating="2700"
status="Computer" />
<player id="mertz"
name="David Mertz"
rating="1400"
status="Amateur" />
</players>
<matches>
<match>
<Date>2002-10-04</Date>
<White refid="fritz" />
<Black refid="kramnik" />
<Result>Draw</Result>
</match>
<match>
<Date>2002-10-06</Date>
<White refid="kramnik" />
<Black refid="fritz" />
<Result>White</Result>
</match>
</matches>
</club>
|
上記のXMLデータ表現は、まったく明確です。PerlおよびPythonの例で挙げたネイティブのデータ記述に比べてまったく冗長なところがありません。リスト5 のYAMLの記述と比べても同じことが言えます。テキスト・エディターのような汎用ツールを使って文書を修正するのもそれほど困難ではありません。(実際、最初はまさにこのようにしてXMLを作成しました。)
セマンティックに言って、上記のXMLの提案には、先に論じた問題がすべて含まれています。プレーヤーは、実際には順序付けの対象ではないのに、順序付けされているように見えます。また、プレーヤーのリストの後に対局リストが置かれていますが、実際にはそのような概念上の順序が意図されているわけではありません。プレーヤーの属性には、希望どおりに順序がありませんが (XML属性であるため)、対局の「属性」はXML属性には収まらないので、人工的な順序が押し付けられています。
より重要な問題は、この最適なXMLフォーマットを実際に読み書き することに伴って生じます。一般的なXML APIの中には、この操作を自動化することに近づいているものさえ1つもありません。たとえば、SAXリーダーは、さまざまな「プレーヤー」と「対局」イベントを探して、関係するネスト辞書またはリストに手動で追加することはできるかもしれませんが、このアプローチはもろいものであり、開発中にデータ構造にほんのわずかな変更が加えられただけでも、プログラムのやり直しが必要です。DOMツリーをたどることにも同様の問題があります。JDOM またはREXML などのカスタムAPIのどちらも、あまり助けにはなりません。gnosis.xml.objectify は、ネイティブ・オブジェクトを自動生成する点で大変良い仕事を行いますが、XMLを読み取ることしかできず、これを書き戻すことはできません。当然のことながら、書き込むことは、それに伴うあらゆるもろさがあるとは言え、読み取ることと対になるものです。
YAMLフォーマットは、動的な言語のデータ構造と、直感的により良く適合しています。見た目も、より優れています。ここに、同じチェス・クラブのデータをYAMLで表現したものを示します。
リスト5. YAMLで記述したチェス・クラブのデータ
---
players:
Vladimir Kramnik: &kramnik
rating: 2700
status: GM
Deep Fritz: &fritz
rating: 2700
status: Computer
David Mertz: &mertz
rating: 1400
status: Amateur
matches:
-
Date: 2002-10-04
White: *fritz
Black: *kramnik
Result: Draw
-
Date: 2002-10-06
White: *kramnik
Black: *fritz
Result: White
|
このフォーマットには、優れた点がたくさんあります。YAML Webサイトには正確な仕様書が掲載されていますが (参考文献を参照)、この簡単なサンプルを見れば、基本的な要素の概念をかなり正しく理解することができます。仕様には、(複数) 段落のストリングを組み込むための、直観的な方法も含まれています。YAMLは簡潔なものですが、それでもなお読みやすいものです。その上、引用符の使用は最低限のもので、データ型はパターンから推測されます (たとえば、日付のように思えるものは、明示的にストリングとして引用符で囲まない限り、タイム・スタンプとして扱われます)。あらゆる名前付きのターゲットへの参照を使用することができます。さらに、意味深いこととして、YAMLは順序付きの集合と連想集合の区別を保ちます。追加のボーナスとして、YAMLはテキスト・エディターで非常に簡単に編集できます。
実のところ、上記のセマンティック上および構文上の利点は、このアプリケーションでYAMLを使用する最も大きな理由ではありません。実は、最良の点は、サポートされている言語すべてに渡ってインターフェースが一様であるということです。上記のYAMLデータ・ファイルの読み取り、操作、および書き込みは、以下のようにして非常に簡単に行うことができます。
リスト6. PythonでYAMLデータ・ソースにアクセスする
import yaml
club = yaml.loadFile('club.yml').next()
# ...manipulate the 'club' data structure...
club_yamlstr = yaml.dump(club)
# ...do something w/ formatted YAML in club_yamlstr...
|
上の例では.next() メソッドを使用しています。なぜなら、YAMLのテキストには、それぞれ--- で区切られた、複数のストリームが含まれている場合があるからです。偶然にも、club のデータ構造は、先に純粋なPythonの定義で定義したデータ構造とまったく 同じです。
Perl (またはRubyあるいはJavaプログラミング) でも、ステップはほとんど同じです。
リスト7. PerlでYAMLデータ・ソースにアクセスする
use YAML ();
my $club = YAML::LoadFile( 'club.yml');
my $club_yamlstr = YAML::Dump($club);
|
YAMLのデータ構造とネイティブのデータ構造との間の行き来は自由…、と言うか、それに非常に近いものです。小さな欠点を2つ見つけました。
- 参照の名前 (たとえば "*kramnik") が失われて、単なる番号 (たとえば、"*1") になる。
- ターゲットが、最初に現れる位置では、必ず完全にスペルアウトされる。
美的な観点から、プレーヤーの詳細が "players" セクションに現れるのが筆者の好みですが、順序なしの辞書の場合、これは保証されません (Perl/Pythonのサンプルで_player を使用するのが、物事を強制的に操作するためのうまい方法です)。
ここで扱っていないYAMLの機能はたくさんあります。公式の仕様書は、(たいていの仕様書と同様) いくらか読みにくいものですが、優れたものです。たとえば、既存のYAMLライブラリーには、XMLとYAMLの間で行き来するための十分な (優秀とはいきませんが) 変換ツールが付属します。また、YPATHと呼ばれる技術 (XPATHのYAML版) のサポートもあります。
この紹介記事の意図は、YAMLがXMLより優れたオブジェクト直列化フォーマットを提供するいくつかの状況を提案することでした。筆者の心の中では、XMLが常にデータ表現の最良の選択であるとは限りません。そのことが明白ではない多くの状況においてさえもです。
- YAMLのホーム・ページはhttp://yaml.org/ です。
- YAMLの仕様書は最近1.0レベルに達しました。http://yaml.org/spec/ で、その全盛期にあるのを見ることができます。
- 筆者が以前に書いた複数のコラムで、関連トピックを扱っています。
- REXMLは、XMLをもっと「ネイティブ」データ構造らしく見せるためのRubyライブラリーです。REXMLライブラリー をご覧ください。
- PYXは、まったくのXMLというわけではないものの、いろいろな意味で処理が容易なもう1つのフォーマットです。しかし、PYXのセマンティクスは本質的に言ってXMLと同一であり、異なるのは構文だけです。PYX入門 をご覧ください。
- XML-RPCのオブジェクト・モデルを調べて、
gnosis.xml.pickleと比較しました。後から考えてみると、筆者は (控え目に言ってもたくさんの理由で) 両者のどちらよりもYAMLを好んでいます。オブジェクト・モデルとしてのXML-RPC をご覧ください。 - 筆者のPythonツール
gnosis.xml.pickleおよびgnosis.xml.objectifyは、XMLと動的なプログラム言語 (具体的に言えば、最低でもPython) との間の概念的なギャップの橋渡しを試みたものです。xml_pickleおよびxml_objectifyの再考 をご覧ください。
- 「XMLの論考」のコラムすべては、以下のとおりです。
- XML文書をオブジェクトとして'Python 的に'取り組む
- XML文書をオブジェクトとして "Python 風" に処理する方法 (II)
- XMLの派生語であるDocBookの紹介
- DocBookのXML版を楽しむ
- XSLTを使ってDocBook文書を変換する方法
- さまざまなエディター
- W3C XML SchemaとDocument Type Definitions (DTD) の比較
- XMLを階層モデル、リレーショナル・モデル、オブジェクト指向モデルに適応させる
- SQL照会からのDTDおよびXML文書の生成
- XML文書を索引付けする
- xml_pickleおよびxml_objectifyの再考: オープン・ソースのレッスンと常識
- Pythonモジュールxml2sqlおよびdtd2sqlの使用: DTDおよびXML文書からのSQLステートメントの生成
- XMLと圧縮: 文書のエントロピーの調査
- DOM、SAX、およびXSLTの限界を超える: XML用のHaXml関数型プログラミング
- オブジェクト・モデルとしてのXML-RPC: 大衆のためのデータ・バンドルか
- もう1つのPython/XMLツール・セット4Suite: 体重400キロのゴリラのようなPython XMLツールの紹介
- PYX入門: 行指向のXML
- REXMLライブラリー: Rubyプログラミング言語におけるXML処理
- 続・XMLと圧縮: ブロック・レベルのアルゴリズムとリソース負荷
- gnosis.xml.validityライブラリーによって妥当性を確保する: OOPデータをXMLの規則に押し込む
- XMLエディターの総まとめ: 第1回: JavaおよびMacOS用の製品を再び取り上げる
- XMLエディターの総まとめ: 第2回: Windows用エディターを再び取り上げる
-
developerWorks XMLゾーンで、さらにXMLの参考文献を調べてください。
-
XMLおよびその関連テクノロジーのIBM Certified Developer になる方法をお調べください。
