<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/xml.xsl" type="text/xsl"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title>nulog, NULL::something : out of the headphone &gt; 2006 &gt; March &gt; 16 &gt; XPath, $X function, NSResolver</title>
  <link href="http://lowreal.net/logs/2006/03/16/1"/>
  <icon>http://lowreal.net/img/banner.png</icon>
  <link rel="self" type="application/atom+xml" href="http://lowreal.net/logs/2006/03/16/1.atom"/>
  <link rel="alternate" type="application/xhtml+xml" href="http://lowreal.net/logs/2006/03/16/1.xhtml"/>
  <updated>2006-03-16T23:06:04+09:00</updated>
  <author>
    <name>cho45(砂糖)</name>
  </author>
  <id>http://lowreal.net/2006/03/16/1</id>
  <entry>
    <title>XPath, $X function, NSResolver</title>
    <link rel="alternate" type="text/html" href="http://lowreal.net/logs/2006/03/16/1.html"/>
    <link rel="alternate" type="application/xml+xhtml" href="http://lowreal.net/logs/2006/03/16/1.xhtml"/>
    <updated>2006-03-16T23:06:04+09:00</updated>
    <published>2006-03-16T23:06:04+09:00</published>
    <id>http://lowreal.net/2006/03/16/1</id>
    <category term="xpath"/>
    <category term="js"/>
    <content type="xhtml" xml:base="http://lowreal.net/">
      <div xmlns="http://www.w3.org/1999/xhtml">
        <p><a href="http://lowreal.net/logs//2006/03/13/3">JS の XPath</a> なんて書きましたけど、重大なバグがありまして、っていうかなんで気がつかなかったんだろう、えーそれは application/xhtml+xml なページ、すなわち <abbr title="Extensible Markup Language">XML</abbr> として、名前空間をちゃんと扱うページではまともにセレクトできないんですよーははははー、例えばこのサイトとかね。</p>
        <pre class="ECMAScript">
$X = function (exp, context) {
    if (!context) context = document;
    var resolver = function (prefix) {
        var o = document.createNSResolver(context)(prefix);
        return o ? o : (document.contentType == "text/html") ? "" : "http://www.w3.org/1999/xhtml";
    }
    var exp = document.createExpression(exp, resolver);
    
    var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
    switch (result.resultType) {
        case XPathResult.STRING_TYPE : return result.stringValue;
        case XPathResult.NUMBER_TYPE : return result.numberValue;
        case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
        case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
            result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
            var ret = [];
            for (var i = 0, len = result.snapshotLength; i &lt; len ; i++) {
                ret.push(result.snapshotItem(i));
            }
            return ret;
        }
    }
    return null;
}

alert($X("//x:p")); // Array of p elements
alert($X("count(//node())")); // =&gt; node number
alert($X("count(//x:body) = 1")); //=&gt; must be true

</pre>
        <p>かなり強引に修正してみた。</p>
        <p><abbr title="XML Path language">XPath</abbr> で要素を指定するとき <samp>x</samp> という prefix (上のコードの場合は別になんでもいいんだけど、普通は x とか xhtml とかいうのをつける) を必ずつけるようにしとく。使い勝手が悪くなったけど、仕方ない。prefix がないときは resolver をよんでくれないみたいだ。</p>
        <p>そう、で、resolver なんだけど、実はただの関数だった。<code>evaluate</code> は prefix を見つけると、resolver に prefix を渡し、URI を返すように要求する。resolver は prefix に対応する <abbr title="Uniform Resource Identifer | Universal Resource Identifer">URI</abbr> を返す。<code>null</code> の場合はエラー, <code>""</code> の場合は、名前空間が null のものとして扱われるみたい (要追試)。</p>
        <p>つまり、上のコードの resolver がやってることは、とりあえず普通の場合のように NSResolver を作って投げてみて、ダメだったら contentType にあわせて名前空間を返してやるっていう、かなり強引な (二回目) 方法なわけです。誰かもっと美しくして！</p>
        <ins datetime="2006-03-16T23:24:33+09:00">
          <p><q>使い勝手が悪くなったけど</q> と書いたけど、<abbr title="XSL Transformations">XSLT</abbr> で使うような XPath と同じになった。まぁ名前空間を考慮するとこういうことになるっていう名前空間マジックなんだけど、やっぱり面倒くさいよなぁ。</p>
          <p><code>$X("count(//x:body) = 1")</code> を <code>$X("count(//*[local-name() = 'body' and namespace-uri() = "http://www.w3.org/1999/xhtml"]) = 1")</code> みたいに書きたくはないし、<abbr title="Hyper Text Markup Language">HTML</abbr> なページと <abbr title="Extensible Hyper Text Markup Language">XHTML</abbr> ページとで、同じ XPath を使おうとするとこんなもんになってしまうような気もする。</p>
        </ins>
        <ins datetime="2006-03-17T02:41:30+09:00">
          <p>冷静に考えると <samp>x</samp> より <samp>h</samp> のほうがいいや。</p>
        </ins>
      </div>
    </content>
  </entry>
</feed>
