2009-06-28

XPathは、XHTMLにおいて使う方法が分からない。

XHTML上でXPathを使おうと試みているが、なかなかうまくいかない。

HTML上なら、何の問題もない。ただ、XHTML(Content-typeが、application/xhtml+xmlである本物のXHTML)で、XPathを使うのが、やたらと難しい。

例えば以下のコード

document.evaluate("count(/html/*)", document, null, XPathResult.NUMBER_TYPE, null).numberValue ;

このコードを、html下にheadとbodyのある、いわゆる一般的なXHTMLに対して適用すると、2が帰ってくるはずである。実際、HTMLでは、2が帰ってくる。ところが、本物のXHTMLでは動かない。

どうやら、名前空間とやらが問題らしい。そこで、以下のコードを試してみた。

var result = document.evaluate("count(/html/*)", document, document.createNSResolver(document), XPathResult.NUMBER_TYPE, null).numberValue ;
var result = document.evaluate("count(/html/*)", document, document.createNSResolver(document.documentElement), XPathResult.NUMBER_TYPE, null).numberValue ;

動かなかった。そこで、XPathNSResolverインターフェースを自前で実装することにした。

// http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathNSResolver
// に準拠した実装
var NSResolver1 =
{
        lookupNamespaceURI : function (prefix)
        {
                if ( prefix === "html" )
                        return "http://www.w3.org/1999/xhtml" ;
                else 
                        return null ;
        }
} ;

// たんなる関数
function NSResolver2 (prefix)
{
        if ( prefix === "html" )
                return "http://www.w3.org/1999/xhtml" ;
        else 
                return null ;
} ;

どちらを使っても、Chrome、Safari、Firefox、Operaでは動かなかった。

どうしたら動くのだろう。もうお手上げだ。

というかこの場合、名前空間をvalidateすることを望んでいるプログラマはいるのだろうか。普通、速度を気にするはずである。XPathが遅いの早いのと議論されているようだが、どう考えてもこれは、机上の空論による規格としか思えない。実際、誰も使っていないじゃないか。みんな何を使っているかというと、jQueryのようなJavascriptのライブラリを使い、CSSのセレクタで要素を取得しているのだ。実際、要素を検索するといっても、それほど複雑な検索機能を欲してはいないのだろう。所詮はCSSのセレクタ程度で十分だということだ。

そして、実際XPathは遅いと思う。なにしろ、CSSのセレクタのように、単純に1パスというわけにはいかない。場合によっては、ノードツリーをさかのぼって探さなければならないし、その規格はやたらと複雑だ。一体誰が使うのか謎な機能が多すぎる。

XPathは、どう考えても失敗規格だなぁ。第一、XPathの表現は読みにくい。

6 comments:

os0x said...

XPath側にprefixがないとどうしようもないです。
手抜きですが、これで一応動くかと。
document.evaluate("count(/xhtml:html/*)", document, function(prefix){ return document.documentElement.namespaceURI; }, XPathResult.NUMBER_TYPE, null);

手前ですが、汎用的なものは http://d.hatena.ne.jp/os0x/20081007/1223350124 が参考になるかと

os0x said...

そうそう、HTML と XHTML で同じ XPath を使えるようにしようというアプローチもあります。
http://nanto.asablo.jp/blog/2008/12/11/4003371

Anonymous said...

またまた、ちょっと自分が使いこなせないと規格にけちつけるわけですね。

hito said...

結局、本物のXHTML上で、名前空間指定をせずにXPathを使う方法はないんですかね。
一体全体どこの誰が、検索対象のXML上では無名の名前空間として使っているノードに対して、名前空間を指定した検索を行いたいというのか。
検索対象のXMLでは、明示的な名前空間を使っていないのです。
なぜ、わざわざ指定しなければならないのか。

そのリンク先の解決方法は、実に馬鹿げています。
本来、名前空間など使ってないドキュメントに対して、なぜ適当な一文字の名前空間をでっち上げて、
それに対して、愚直に名前空間URIを返す、これまた無意味なXPathNSResolverを実装しなければならないのか。

それに、こんなバカなコードでも動くわけです。

//どんな名前空間に対してもXHTML名前空間を返すアホなXPathNSResolverの実装
var NSResolver =
{
lookupNamespaceURI : function (prefix)
{
return document.documentElement.namespaceURI ;
}
} ;

var result = document.evaluate("count(/寿限無寿限無五劫の擦り切れ:html/*)", document, NSResolver, XPathResult.NUMBER_TYPE, null).numberValue ;

こんな馬鹿げたコードでも動くのに、名前空間字体を省略することはできない。
XPath自体は悪くない規格なのに、APIとしての規格と実装がひどすぎる。
どうりで誰も使わないわけですな。
一体この規格を制定した奴らは、何かとてつもなくいいものを吸っていたに違いない。

Anonymous said...

もうちょっと謙虚な姿勢でも罰は当たらないのでは?

> どうりで誰も使わないわけですな。
事実誤認もいいところです。

> HTML上なら、何の問題もない。
XPathはHTMLにつかうものじゃないです。
ここからして間違っているので、XHTML内で名前空間の問題に引っかかったくらいで
文句たらたらなのだろうなぁ。

もう少し、他の人もせめてあなた程度には賢いという前提で規格を眺めてみてはどうです?
そうしたら、こうなっているのには何か理由があるに違いないと少しは考えることでしょう。

名前空間prefixを省略できてしまうと、XSLTでXPathを使った場合にどういう問題が起きるか?
くらいは考えてみてください。

hito said...

私はXPathそのものではなく、各種ブラウザのDOM XPathの実装について話しているのです。
確かに、DOMというのは独立した規格とはいえ、その目的のひとつは、やはりHTMLやXHTMLに対して使うというものがあると思います。
だからこそ、ブラウザもDOM XPathを実装していて、Javascriptから使えるようになっているのです。

しかし、実際には、このような問題の本質ではない仕様によって、非常に使いづらくなっているなっているのです。

だからこそ、jQueryのようなライブラリが乱立し、
ついには、QuerySelectorAllのような規格ができあがるのです。

DOM XPathがもう少し使いやすいAPIになっていれば、CSSのセレクタを要素のマッチに使用することはなかったはずです。