B.4 Namespace URI Lookup(Namespace Algorithms)

Node.lookupNamespaceURIメソッドはこのように名前空間URIを解決するらしい。実装がこの手順をきちんと守るのであれば、先ほどのようなものに書き換えても問題は起こらないはず。むしろ軽量化を図れるのではないか。逆に、evaluateメソッドでノードテストのQNameを評価する際に、毎度このプロセスが行われるのだとしたら、相当に遅くなるのではないか。

なんだかいい加減な用語連発でダラダラ書いてしまったけれども、きちんと纏めておきたいところ。

Hawk'sW3L: XMLメモ

prefix無しの名前空間をもったノードをQNameで表現できないなんて馬鹿な話があったとしたら、DOM level 3 XPathは屑だと思った。まだ勧告されていないけれど。

全く信じられなかったので、読みたくないDocument Object Model (DOM) Level 3 XPath Specification(31 March 2003)を真面目に読んでみたのだけれど、もう驚きの一言。目が覚めたよ。

隅から隅まで読んだけれど、「QName」の一言も出てこない。prefixから名前空間を解決する際、Node.lookupNamspaceURIメソッドと似た方法で行うそうで(それくらいしか書かれていない)、この引数にprefixを与えると、そのノードのスコープ内でprefixで関連付けられた名前空間URIを得られる。このメソッドがprefix無しの名前空間URIを得るには、引数にnull値を与えるのだそうだ。しかしこのnullをXPathでどう表現するのか。そんなデータ型はノードテストのQNameで表現できない。それどころかXPathにもない。

そもそも「XPathNSResolver」が名前空間の解決をするというのに、このインターフェイスを持ったオブジェクトを作成する唯一のメソッド、 DOMDocument.createNSResolverの引数がNodeだというのだから、これはもう、名前空間接頭辞を事前に知らなければDOMでXPathを使えないということでは。悪あがき的に、node = document.createElementNS('http://www.w3.org/1999/xhtml', 'xht:div'); を作成して、node.lookupNamespaceURI('xht')を試してみたのだけれど、駄目だった。少なくともMozillaの実装では。もし可能だったとしても、prefixと名前空間URIを関連付けるためにノードを作成するなんて馬鹿げた話もない。

で、私の結論としてはこれを解決するにはXPathNSResolverインターフェイスと同じメソッドを持ったオブジェクトを自作すること。JavaScriptで書くと例えばこんな風になる:

var nsresolver = {
  lookupNamespaceURI:
    function(prefix){
      switch(prefix){
        case 'xht':
          return 'http://www.w3.org/1999/xhtml';
        default:
          break;
    }
  }
};

これをXPathNSResolverとして渡せば、少なくともMozillaではQName xht:htmlを XHTMLのhtml要素として扱ってくれた。要するに引数のlookupNamesaceURIメソッドを呼んでいるだけであって、型のチェック等はしていないらしい。多分、「本物の」lookupNamespaceURIメソッドを書き換えても大丈夫だろうというかそっちの方が良い。以下実験したコード:

var de = document.documentElement;
var type = XPathResult.FIRST_ORDERED_NODE_TYPE;
var result = document.evaluate('/xht:html', de, nsresolver, type, null);

参照している「XMLメモ」では、XPathExpression インターフェイスを持ったオブジェクトを生成しているけれども、これはXPath式を使いまわす際に有効な方法で、一度しか使わないならXPathEvaluatorのevaluateメソッドを使うこともできるようだ。上の「document.evaluate()」がそれ。私の場合はそのような使い方のほうが多いと思う。

しかしこんな引数が四つも五つもあるような汚いインターフェイスは常用したくないから、当然別の何かを被せて使用することになる筈。

あーもう驚いた。

ちなみにDOM Level 2/3 標準技術をMSIEで使う(XML読み込みとXPath) @ レナ姫のWeb研究室経由。でもノードテストに主ノード型しか使えないXPathなんて駄目でしょう。絶対に何とかすべきです。

つづきというか修正

function _XPathNSResolver(nsmap, nodeResolver){
  this._nsmap = nsmap;
  this._resolver = nodeResolver?
    nodeResolver.ownerDocument.createNSResolver(nodeResolver) : null;
}

_XPathNSResolver.prototype.lookupNamespaceURI = function(prefix){
  var v;
  if (v = this._nsmap[prefix])
    return v;
  if (v = this._resolver)
    return v.lookupNamespaceURI(prefix);
  return v;
};

var de = document.documentElement;
var type = XPathResult.FIRST_ORDERED_NODE_TYPE;
var nsresolver = new _XPathNSResolver({xht: "http://www.w3.org/1999/xhtml"}, de);
var result = document.evaluate('descendant::xht:*', de, nsresolver, type, null);