あけましておめでとうございます。
去年(2002年)の記事を整理してみましたが、XPathなるカテゴリを作っていたことをすっかり忘れていました。何故そんなものを作ったのかと自分を問い詰めたい気分ですが、今更遅いので、無理矢理XPathの記事でも書いてみようと思います。
適当にXPath1.0を勉強すると、応用する際に困るのではないかと思います。
例を挙げます。
このような省略だらけの方法を覚えても、全然応用が効かないのです。「class属性を持ったp要素を特定するには、p[@class]と書けばよいのです。分かりやすいでしょう?」うんぬん。しかし、例えばこの例とは逆に、class属性を持たないp要素を指定することを考えてみて下さい。まったく別のことを覚えなければなりません。
述語[1]と[@class]について考えてみます。一体、[]の中身はどのようなデータ型になっているのでしょうか。1は数値だし、@classは、attribute::classの省略形で、軸とノードテストで構成されており、classという属性ノードを示しています。述語には、他にどのような型が許され、どのように解釈されるのでしょうか。茫然としませんか?
しかし、省略をせず、見た目の分かりやすさを諦めれば、述語内に記述が許される(実際には勝手に変換される)のはブール型であるということが一目で分かります。それさえ分かれば、応用の幅がぐんと広がるはずです。
上のXPath(ロケーションパス)は、まずルートノードとその子孫ノード全ての集合に焦点を合わせ、それら各ノードの子供の内、最初に登場するdiv要素に焦点を合わせ、それらの各div要素の子供の内、class属性を持っているp要素に焦点を合わせています。
一見分かり難いのですが、結局のところ、このような書式を理解できなければ殆ど応用が効かないのです。だったら最初からきちんと覚えるべきではありませんか? 一案としてはこの例のように、比較演算子を用いた式の形にしておくと良いのではないでしょうか。
意図せぬ「型変換」も予防しておきたいところです。例えば:
これは、'note'という文字列とclassという属性ノードを比較しているように見えますが、内部ではstring関数で型変換が行われています。キッチリ書くとこのようになります:
boolean関数、string関数の性質についてはXML Path Language (XPath) (英語) を参照して下さい。
「文書解析順に見て一つ目と二つ目のdiv要素の子供で、class属性を持たないp要素」は次のように指定できます。:
書き方は他にも考えつきますが、しかしどうでしょう。最初に挙げたような簡単な書式(//div[1]/p[@class])から連想できるでしょうか。私には無理です。それから、「 // 」は確実に意味が分かるまでは絶対に使うべきではありません。例えば//div[1]は、最初に登場するdiv要素ではありません。//でツリー上の全てのノードを指定し、そしてそれら全てのchild::軸を見ています。
因みに、XPathでより正確なノードリストを指定することができれば、XSLTスタイルシートを書く際の負担が激減します。特に構造化スタイルシートを書く際には死活問題で、XPath1.0を使いこなしてもまだ機能的に足りないくらいです。正規表現が使えないのはともかくとして、式によって返却されたノードリストは和集合しか取れない、コンテキストノードを操作できない、等々。position関数の出来の悪さには頭にきます。
DOMの文脈にてXPathを気軽に使えるよう、カスタムパネルJtrEntに操作用インターフェイスを組み込んでおきましたので、fub_netユーザな方は、色々試してみては如何でしょうか。
var xml = new JtrXML('http://www.w3.org/');
var doc = xml.getDocument();
これで変数docはhttp://www.w3.org(XHTML文書)のDocumentへの参照になり、かつ、XPathを使用するためのプロパティも設定が済みました。Document.selectNode(XPath)でノードリストを参照できます。
文書制作者が指定した名前空間接頭辞がそのまま使えますが、デフォルトの名前空間は、「defaultNS」を名前空間接頭辞にしてあります。http://www.w3.org(XHTML)の場合には、全ての要素はhttp://www.w3.org/1999/xhtmlという名前空間を(接頭辞無しで)もっていますから、ノードテスト(QNameです)には必ずdefaultNSという接頭辞をつけます。例示します:
var xml = new JtrXML("http://www.w3.org/");
var doc = xml.getDocument();
var sXPath = "/descendant::defaultNS:a[boolean(attribute::rel)]";
var nl = doc.selectNodes(sXPath);
rel属性を持ったa要素で構成されるノードリストを変数nlで参照しました(因みにこのa要素は10個見つかりました)。
つまらない例だな。
ではW3Cホームページの「ニュース記事」を保存してみましょう。マイ コンピュータ(謎)のセキュリティ設定を低くしているなら、保存用のsaveメソッドが使えます。インスタンスを生成することで、JtrXMLオブジェクトと、それから、Stringオブジェクトにも(笑)実装されますので手軽に使えます。
まずニュース記事を特定しましょう。
まあ普通に「ソース」を見ても良いのですが、私は正にこのためにDOM Inspectorを作ったのでして 。ともかく、これでニュース記事は「class="newsBlock"」という属性を持ったdiv要素でグループ化されていることが分かりました。[JScript]ボタンを押し、次のスクリプトを書きます。
var xml = new JtrXML("http://www.w3.org/");
var doc = xml.getDocument();
var sXPath = "/descendant::defaultNS:div[string(attribute::class) = 'newsBlock']";
var elmDiv = doc.selectSingleNode(sXPath);
これでニュース記事のdiv要素を変数elmDivで参照することができました。XMLソースはNodeインターフェイスのMS独自拡張、xmlプロパティで参照でき、Stringオブジェクトになっています。
var path = 'C:\\temp\\w3c_news.txt'
elmDiv.xml.save(path);
これで保存完了です。パスは適当(好い加減と言う意味ではない)なものにしてください。尚、saveメソッドは私による独自拡張(笑)なので気をつけてください。
と、このようにMIME typeの判別を代理人任せにしなければ、text/htmlでserveされていたとしてもXHTML文書はご立派なXML文書なのですよ(これが言いたかったんです実は)。Wired (英語)がXHTML+CSSでリニューアルされた時、私がどれだけ嬉しかったか分かっていただけるでしょうか。