FirefoxのE4XはXPathより便利?

つい最近FirefoxのXMLオブジェクトがすごく便利なのを知りました。そして、そのXMLオブジェクトサポートのことをE4X(ECMAScript for XML)と呼んでいたというのを今知りました....

E4Xについては ECMAScript for XML (E4X) 仕様邦訳 を翻訳された nanto_viさんが書かれている E4X in Firefox が詳しいです。

しょせんはさっきE4Xがなんなのかを知った程度なのでちゃんとしたことは他のページを参照していただくことにして、11.2 左辺式 に書かれているアクセサと選別述語演算子でXPathと同じようなことができて、それがXPath以上に便利なところがありますよ、というはなしだけ書こうと思います。

E4XについてECMAScript for XML (E4X) 仕様邦訳序文には
このプログラミング言語拡張は、世界中のもっとも大きな開発者コミュニティのひとつの既存の知識と技術を活用することで XML の学習曲線を平坦化させる、単純で親しみやすい汎用 XML プログラミングモデルを提供するために設計された。
と書かれています。javascriptでXPathを使おうとするとdocument.evaluate()を使って帰ってきたイテレータをまわしたりtry/catchしないといけなかったりしますが、序文の通りE4Xはそのへんがめちゃめちゃ簡単にできます。

RSSをパースする

たとえばRSSをパースしてエントリのURLを取り出したいとします。RSSのバージョンが2.0だとこんな構造になっています。
<rss version="2.0">
  <channel>
    <title>bits and bytes</title>
    <link>http://labs.gmo.jp/blog/ku/</link>
    <item>
      <title>FirefoxのlivehttpheadersでHTTPリクエストの中身が見られるしくみ</title>
      <link>http://labs.gmo.jp/blog/ku/2007/08/firefoxlivehttpheadershttp.html</link>
    </item>
    <item>
      <title>Firefoxのsqliteデータベースの中身を表示する Storage Inspector</title>
      <link>http://labs.gmo.jp/blog/ku/2007/08/firefoxsqlite_storage_inspector.html</link>
    </item>
  </channel>
</rss>
このXMLが文字列として raw_xml_string という変数に入っているなら
var rss = new XML(raw_xml_string);
var links = rss..item.link
for ( var i = 0; i < links.length(); i++ ) {
  do_something_on( links[ i ] );
}
というふうにXPathっぽく書くことができます。 XMLオブジェクトの変数名の後に . と .. とでタグ名を書いていくことで目的の要素を取り出すことができます。XPathの/を .に置き換えただけくらいで書くことができます。結果を配列風に使うときには、長さを得るのにlengthプロパティではなくlength()メソッドを使う必要があり間違いやすいので常に for each を使う方が安全でしょう。(11.2.2 関数呼び出し) for eachを使うと
for each ( var e in links ) {
  do_something_on( e );
と書けます。ちなみにXMLオブジェクトはArrayとは別物なため、評価結果をforEachでイテレートすることはできません。残念。

特定のアトリビュートを持つタグだけにマッチさせるには

さてさてRSSの例は特定のタグを持ってくる、というものでした。でも実際にXPathを使うときには特定の条件に当てはまるタグを持ってくることが多いです。E4XはXPathと比較して基準点のサポートが少ないため表現力で劣る部分もありますが、そのかわり比較関数に任意のjavascriptを記述することができます。これがすごいパワーを秘めています。 以降さわやかな例を思いつかなかったので、以下のような元素の名前と常温での状態を表したXMLを例にしてはなしをすすめていきます。
var e = <elements>
  <element id="He" state="gas"><name><ja>ヘリウム</ja></name></element>
  <element id="Na" state="solid"><name><ja>ナトリウム</ja></name></element>
  <element id="Cl" state="gas"><name><ja>塩素</ja></name><ref url="http://en.wikipedia.org/wiki/Chlorine">Chrorine</ref></element>
  <element id="Hg" state="liquid"><name><ja>水銀</ja><en>quicksilver</en></name></element>
</elements>
このリストから常温で気体のものを取り出す式は e.element.( @state == "gas" ) で表現することができます。これはXPathでは //element[@state="gas"] と表現できます。E4Xでは式を書くので == なのに気をつけてください。XPathのつもりで=にするとアトリビュートに値が代入されてしまいます。(これのことについてはまたあとで触れます) stateがgasなものが選択されるので、結果は
<element id="He" state="gas">
  <name>ヘリウム</name>
</element>
<element id="Cl" state="gas">
  <name>塩素</name>
  <ref url="http://en.wikipedia.org/wiki/Chlorine">Chrorine</ref>
</element>
になります。 もうひとつ簡単な例をあげておきます。常温で気体なもののうち、リファレンスがあるもののURLを取り出したいときには e.element.(@state == "gas" ).ref.@url と書きます。条件式の後にもふつうにドットで繋げて式を書いていくことができます。XPathでも同じように //element[@state="gas"]/ref/@urlと書くことができます。 この式は
http://en.wikipedia.org/wiki/Chlorine
を返します。


これくらいならXPathでできることがちょっとだけ楽になる程度です。

条件にjavascriptで式を書く

E4XでXPathとおなじことができるよ、というのはこれくらいにして、ここからXPathでできないことがE4Xでできるというはなしをしていきます。E4XではXPathと違って条件式の部分に任意のjavascriptを記述することができるので特定の正規表現にマッチするものだけ、という記述が可能です。不自然な例になってしまいますが、元素記号が e で終わるものを取り出したいとします。これは
e.element.( @id.match( /e$/ )
で可能です。eで終わるのはHeのヘリウムしかないので、結果は
<element id="He" state="gas">
  <name><ja>ヘリウム</ja></name>
</element>
になります。XPathでは正規表現がサポートされていないので同じことはできません。(この例は例がよくないのでXPathでもやればできそうですが...) さらにE4Xで式に書けるのはコンテキストノードのアトリビュートだけではありません。ツリー構造の一番内側のnameタグに入っている日本語での名前のその最後がいかにも元素の名前っぽい"ウム"で終わるelementを取り出す式を
<pre><code>e.element.(  name.ja.match( /ウム$/ )</code></pre>
<element id="He" state="gas">
 <name><ja>ヘリウム</ja></name>
</element>
<element id="Na" state="solid">
  <name><ja>ナトリウム</ja></name>
</element>
と、簡潔に書くことができます。XPathではこの子孫ノードの特定の条件で判定するというのが簡単には表現できません(よね?)。


ちなみに、なんと表現したらいいのかよくわかんないのですが、この式評価時のスコープはコンテキストノードになっていて違いました。なにになっているかはわかりません。アタマにfunction::をつけることで13.4.4 XML プロトタイプオブジェクトのプロパティ (組み込みメソッド) を呼び出すことができます。function::parent()でコンテキストノードの親を参照することができます。先祖すべてを対象にするXPathのancestorのようなものはありませんが、工夫すればエミュレートすることはできるのではないでしょうか。thisが何を指しているかはちゃんと調べていませんが、コンテキストノードを指してはいませんでした。

マッチするタグだけに対して操作を適用する

上で条件式の部分にjavascriptで任意の式が書けることがわかりました。これと最初の方で少し触れた = と書くと代入されますよ、というのを組み合わせると特定の条件をもつタグにだけ操作を適用する、ということができます。 e.element.( @state == "gas" && ( @id = @id.toLowerCase() ) ) とすれば常温で気体の元素だけ元素記号を大文字に変更することができます。 この式を評価すると、元素記号が小文字になった
<element id="he" state="gas">
  <name>
    <ja>ヘリウム</ja>
  </name>
</element>
<element id="cl" state="gas">
  <name>
    <ja>塩素</ja>
  </name>
  <ref url="http://en.wikipedia.org/wiki/Chlorine">Chrorine</ref>
</element>
が帰ってきます。( @id = @id.toLowerCase() )は@state == "gas"がtrueのときのみ実行されるので常温で気体でない元素の元素記号は変化しません。

まとめ

というわけでXPathで複雑になるようなものがE4Xではjavascriptのパワーと相まって極めて簡潔にかけたりするのでいいですよ、というおはなしでした。6 設計方針に
最小量: 適切な場所では、E4X は、現在 ECMAScript オブジェクトを操作するのに利用できない XML を操作するための新しい演算子を定義する。この演算子のセットは不必要な複雑さを避けるため最小限に保持されるべきである。例えば XPath のすべての機能を提供することは E4X の目標ではない。
と書かれている通り、XPathに比べ基準点のサポートが弱いので単体での表現力では劣りますが、その欠点はjavascriptが使えることで十分補えているでしょう。

あとはFirefoxとFlex2くらいでしかサポートされていないのでGreasemonkeyスクリプトを書いたりFirefoxのextensionを書いたりFlexを使ったりするときにしか使い道がないのがなんとかなればと思います。

補足 reference to undefined XML name 問題

subtech - Pink Blossom Diary - E4X で、
これ attr が設定されてないとエラーになっちゃうんだよね。
というつっこみをいただいて、うわーほんと XPath -> E4x -> XPath だ.... と愕然としつつ、なんとかなんないのかなと調べてみたらattrがない場合に reference to undefined XML name と言わせないようにすることができたので補足しておきます。 まずとりあえず
console.log(e.element.( attribute("state") == "gas" ));
はだめなのかと思ってやってみたものの、これは attribute is not defined と言われてだめでした。でも仕様にはattribute関数は存在するし条件式の中でなければ呼び出せるので、なんとかすれば呼べるんだろうとMozillaのソースを調べてみました。 E4Xのテストコードの中でattribute関数のテストも書かれているはずだから、たぶんそのへんでなにかわかるだろうと mozilla/js/tests/e4x をgrepしたところ
Regress/regress-323338-1.js:65:  var el = htmlXML.body.div..div.(function::attribute('id') == 'summary');
という記述を発見。アタマに funciton:: をつけるなんてどこにも書いてなかったぞと思いながら
console.log(
 e.element.( function::attribute("state") == 'gas' )
);
にしてみると、無事エラーが出たりすることなく
<element id="Cl" state="gas">
  <name>
    <ja>塩素</ja>
  </name>
  <ref url="http://en.wikipedia.org/wiki/Chlorine">Chrorine</ref>
</element>
という結果を得ることができました。

E4Xの仕様には function:: をつけるとXMLListの関数を呼び出せるとはどこにも書かれていないのでFirefoxでしか使えないのかもしれませんが、(nanto_viさんにコメントいただいたとおりSpiderMonkeyの独自拡張としてfunctionネームスペースがあるとE4X in Firefox 発表資料に書かれているのを見落としていました。SpiderMonkeyの独自拡張なためFirefoxでしか使えません)とりあえずFirefoxでは function::attribute( .... ) に書き換えればよさそうです。@だけで済んでいたのに比べると極めて冗長ですが....

参考

  • E4X in Firefox 発表資料
  • E4X in Firefox
  • ECMAScript for XML (E4X) 仕様邦訳
  • [JavaScript] ECMAScript for XML (E4X) を試してみた
  • akihiro kamijo: E4X
  • Core JavaScript 1.5 Guide:Processing XML with E4X - MDC

tags

  • E4X
  • Firefox
  • XML
  • javascript
  • 「FirefoxのE4XはXPathより便利?」のはてなブックマーク数
  • 「FirefoxのE4XはXPathより便利?」deliciousブックマーク数
  • 「FirefoxのE4XはXPathより便利?」をはてなブックマークに追加
  • save "FirefoxのE4XはXPathより便利?" to del.icio.us
  • 「FirefoxのE4XはXPathより便利?」をリアルタイムブログ検索
  • permalink
  • FirefoxのlivehttpheadersでHTTPリクエストの中身が見られるしくみ
  • ソーシャルコンテンツ証券市場 meta-markets

comments

TypeKey Enabled
スタイル用のHTMLタグが使えます。
*required
nanto_vi
2007.08.28 01:26

> reference to undefined XML name 問題

一応「E4X in Firefox」に対する解説「E4X in Firefox 発表資料」
http://nanto.asablo.jp/blog/2007/04/22/1459018
にてそのあたりにも触れたつもりです(「SpiderMonkey による拡張」のあたり)。

ku
2007.08.28 13:21

ごめんなさい、ちゃんとSpiderMonkeyの独自拡張としてfunctionネームスペースのことについて書かれているのに見落としておりました... 発表資料のページとあわせて本文を訂正、補足しました。
ご指摘ありがとうございます。

trackbacks

トラックバック元エントリにこのエントリへのリンクがない場合はトラックバックを受け付けません。

http://labs.gmo.jp/mt/mt-tb.cgi/160
©2010 Kentaro Kumagai, GMO Internet Labs., GMO Internet, inc.
bits and bytes
2007 .08. 24 23:08

tagcloud

  • API1
  • C/C++2
  • E4X1
  • FUSE2
  • Firefox18
  • HTML4
  • IE1
  • MySQL1
  • OSX4
  • Opera2
  • PHP4
  • XML1
  • XPCOM4
  • XPath3
  • apache2
  • binary2
  • book1
  • data11
  • debug4
  • design1
  • experiments3
  • extension10
  • google gears1
  • google maps API1
  • greasemonkey3
  • httpd5
  • javascript17
  • linux1
  • logging2
  • mobile3
  • perl4
  • tips4
  • tool11
  • vim2
  • visualization2
  • widget1
  • wii1
  • windows7
  • サービス6
  • 和訳1

Archives

  • 2008.02 (6)
  • 2008.01 (3)
  • 2007.12 (4)
  • 2007.11 (5)
  • 2007.10 (4)
  • 2007.09 (4)
  • 2007.08 (4)
  • 2007.07 (8)
  • 2007.06 (7)
  • 2007.05 (4)
  • 2007.04 (5)
  • 2007.03 (6)
  • 2007.02 (4)
  • 2007.01 (6)

about

  • bits and bytesのXML
  • 「bits and bytes」のCreative Commons
  • Powered by Movable Type
  • イベントと地図 - モグ
  • Use ecto to blog!
  • bits and bytesのはてなブックマーク数
  • bits and bytesをMy Yahoo!に追加
  • Subscribe with Bloglines