もともとXPathっていうのはXSLTとXPointer(何それ?)という2つのXML規格の中で共通していた、XMLノードを指定するクエリ言語を独立で提供しているものだそうな。
http://ja.wikipedia.org/wiki/XML_Path_Language
↑にちょろっとした紹介が書いてある。
それにしてもXSLTはXSLTで興味深い言語で、名前が「Extensible Stylesheet Language Transformation」(意味としては「拡張可能様式票言語・変換編」みたいな感じ)にもかかわらず、スタイルシートというよりむしろXML生成関数型言語のようなマニアが喜ぶ性質をたくさんもっている言語なのだが、今回はそこに深入りしない。何せXPathだけでも十分面白いし。それと関数型言語わかんないし。
一気に要素を取得
話がそれた。とにかく、XPathの面白さはどれだけ構造が複雑になっても一発でデータを取得できることだ。言い換えると構造の変化に強いというか。たとえばこれ
<bind>俺がチップエディタ内TableViewerのために仮組みしたXML定義ファイルだ。基本的に、書いてあるとおりの順番でDataInputStreamからデータを読み込んでいくことだけ想定している。各カラムにアクセスしようと思ったら、このオブジェクトを保持しておけばよいのですよ。
<item id="base" type="byte" />
<item id="half" type="byte" />
<item id="obj1" type="byte" />
<item id="obj2" type="byte" />
<item id="walkIn" type="bool" />
<item id="eventNo" type="byte" />
</bind>
NodeList dataItems = (NodeList) xpath.evaluate("/bind/item", root, XPathConstants.NodeSet);
キャストがあったり謎の変数(root)があったりしてややこしいけど、基本は太字部分。「/bind/item」というクエリをxpathに放り込めば、<item ... /> でくくっているタグ要素全てを持つNodeListを返してくれる。
さてこの構造を利用しようと思ったときにふと気がついた。読込み・書き出しをするデータとTableViewerに表示するデータって、ものが違うじゃん!!
チップエディタに表示させるデータは、強いてさっきと同じXML形式で表現するとこんな感じになる。
<bind>テーブルの表示に必要なのはテーブルヘッダ(CellModifierのプロパティにも使う)と、どのセルエディタを使うかだな。あとDataInputStreamから読み込んだデータはどこに当てはまるのかについても記述が必要だろう。これはid属性で照合すればいいか・・・といろいろ考えて設計すると、もうかなり違うものになってしまった。
<item id="ID" label="ID"/>
<item id="base" label="背" editor="ColorSelect"/>
<item id="halfType" label="向" editor="DirectionSelect"/>
<item id="halfBase" label="斜" editor="ColorSelect"/>
<item id="obj1" label="1" editor="ChipSelect"/>
<item id="obj2" label="2" editor="ChipSelect"/>
<item id="walkIn" label="進" editor="Check"/>
<item id="eventNo" label="EV" editor="Text"/>
</bind>
で、これを二つのXMLに記述してお互い相互作用させようと思うと、XMLドキュメントオブジェクトを二つ用意したり何だりといろいろ忙しい。せっかく多重ネストがXMLの売りなので、一つのXMLにまとめてしまおう。
<bind>と、勢いでネストしてしまったんだけど、こんなときでもデータ部にアクセスするロジックはほとんど変えなくていいってのがXPathのすばらしいところだ。
<data>
<item id="base" type="byte" />
<item id="half" type="byte" />
<item id="obj1" type="byte" />
<item id="obj2" type="byte" />
<item id="walkIn" type="bool" />
<item id="eventNo" type="byte" />
</data>
<view>
<item id="ID" label="ID"/>
<item id="base" label="背" editor="ColorSelect"/>
<item id="halfType" label="向" editor="DirectionSelect"/>
<item id="halfBase" label="斜" editor="ColorSelect"/>
<item id="obj1" label="1" editor="Text"/>
<item id="obj2" label="2" editor="Text"/>
<item id="walkIn" label="進" editor="Check"/>
<item id="eventNo" label="EV" editor="Text"/>
</view>
</bind>
びふぉー:
NodeList dataItems = (NodeList) xpath.evaluate("/bind/item", root, XPathConstants.NodeSet);
あふたー:
NodeList dataItems = (NodeList) xpath.evaluate("/bind/data/item", root, XPathConstants.NodeSet);
な?たったこれだけ。このちょっとした工夫が保障されているだけで、漫然とデータスキーマを決めることができるって確信が持てるわけだ。作りながらデータ定義して、あ、これ間違ったなと思ったら組み替えるっていう怠惰型開発が可能。これ、始め「XMLの強みだなぁ」って思ってたけど、実はXPathが強力だからじゃないかと思って今回の日記はこういう題名にしたわけだ。データ記述言語はXMLのほかにもYAML(Ruby界隈で人気。Railsの設定に使われる)とかJSON(JavaScript界隈で人気。Ajaxで絶賛使われ中)とかもっと便利なものがあるけど、先発のXMLにはXPathみたいな強力な道具があるってのが魅力なのだと思うわけですよ。
面白みはさらに
で、これだけだと「便利だ」だけで「面白い」という感想にはならないんだけど、XPathのすばらしいところにはクエリの方法の多彩さっていうのがある。
たとえば、TableViewer表示要素からバインドされているデータの型を知りたいとき。データのセーブ時などに必要になるんだけど・・・
例 : 今「obj1」要素をDataOutputStreamに保存したいと思っている。TableViewer表示要素を元にデータを処理しているので、現在の処理が知っているタグは
<bind>
<view>
<item id="obj1" label="1" editor="Text"/>
</view>
</bind>
これだけだ。今ほしいのは、idが同じで、呼び出し時に使われたデータのtype属性の値なのだ。つまり
<bind>こいつな。こいつを取得したい場合、XPathでこうやって指定する。
<data>
<item id="obj1" type="byte" />
</data>
</bind>
/bind/data/item[@id="obj1"]/@type
そう、条件付けに属性を使えるのだ。他にも出現順番とかが使えるし、演算もできる。つまり条件に「出現順番が偶数回目のもの」を使えたりするのでとっても便利!ここら辺がまさにクエリ言語の名にふさわしい動きだね。
以下、単なる妄想
これ、同等の働きを配列とハッシュの構造で再現しようとすると、無駄にループを重ねたり余計なクロージャを作ったりしなきゃいけなくて結構大変なのだ。というのも、たぶんRubyとか場合にYAMLをプレーン(YPathなどクエリ言語使わず)に処理しようとすると
temp = []
document["bind"]["data"].
select{|i| i["id"] == "obj1"}.
each{|i| temp.push i["type"]}
return temp
こんな感じで指定しなきゃいけないのかな?あまり詳しくないから間違ってたらごめん。というか、もっと簡易な方法あるかも。とにかく、今俺の知っている知識のなかでは、キーワードが記号(["{などなど)に埋め尽くされてどこを指定しているのかさっぱり不明になってしまう。JavaScriptはハッシュの表記法は簡単だけど、クロージャ表記がややこしいから余計厄介だ。せめてC#3.0のLINQを参考に
documant.bind.data.select{_.id == :obj1}.type
このくらい短くならないか。ん?いや、これLINQでもなんでもないな。何だろ。単なる妄想かな。
そういえば、YAMLにはYPathというXPath相当のクエリ言語があるらしい。まだ仕様策定中だけど。これが整理されたら、単なるネストした配列とハッシュにYPathでアクセスできるのかしらん。YAML由来のものじゃなくても。もしそうだったら俺の中でRuby+YPathの地位がC#+LINQを大幅に超える。最強。
いやこれも単なる妄想だけど。


