Opera 9もXSLTプロセッサが実装されたようですが、Mozilla Firefoxでも実装されているんですね。Netscapeの日本法人が撤退してバージョンアップが止まっているということを(今更)知ったので、Firefox 2.0を入れてみましたが、…うん、これはなかなか。フォント周りも強くなってるし。
Firefox 2のXSLTプロセッサはXSLT 1.0のdocument関数が使えるのはいいんですが、結果ツリーフラグメント(Result Tree Fragment)をノードセットとして評価することができないようです。
そもそも結果ツリーフラグメントって何かというと、大雑把に言えばxsl:variable要素やxsl:param要素の内容としての値です。例えば、
<xsl:variable name="result"> <level>3</level> <number>1-1</number> </xsl:variable>
上記のような変数resultがあった場合、$resultはlevelとnumberの二つのノードを持つノードセットのように見えますが、これはノードセットにはなりません。結果ツリーフラグメントと呼ばれます。結果ツリーフラグメントに対しては文字列操作しか許されず、勿論/や//、[]などのノードセットに対してのみ使える演算子は使えません。ただ、xsl:copy-of要素のselect属性で結果ツリーフラグメントを値に持つ変数を指定すると(<xsl:copy-of select="$result"/>のようにすると)、あたかもそれがノードセットであるかのように振る舞います。
別の例を見てみましょう。対象とするXML文書の子孫にbodyという要素があって、body要素は幾つかのsection要素を子に持っているとします(うちのXML文書のスタイルです)。この時、次のXSLTコード(断片というべき?)は許されます:
<xsl:variable name="body" select="//body"/> <xsl:for-each select="$body/section"> <!-- 何らかの処理 --> </xsl:for-each>
しかし、次のようにすると誤りです。
<xsl:variable name="body"> <xsl:copy-of select="//body"> </xsl:variable> <xsl:for-each select="$body/section"> <!-- 何らかの処理 --> </xsl:for-each>
先程も言った通り、xsl:variable要素などの内容にしてしまうと、それがなんであれ結果ツリーフラグメントになるのです。数値を書いても文字列を書いても何でもかんでも結果ツリーフラグメントに。一方で、select属性で値を与えると、式の型と同じになります。ノードセットならノードセットに、数値なら数値に、文字列なら文字列に、ブーリアンならブーリアンに。そんな訳で、前者の$bodyはノードセットで、後者の$bodyは結果ツリーフラグメントなのです。ああ無情。
しかしながら、xsl:variableの内容として書いてもノードセットとしてみてもらいたいことは少なくないわけで、──例えば変数の値にa要素を幾つか書いておいて、あとでそれをxsl:for-eachで処理するとか、テンプレートの戻り値(戻り結果ツリーフラグメント?)が複雑になる場合、さっきのlevelやnumberのように要素で区切って返すとか(Cで言うと構造体を返す感じ)。そんな時のために、IEやSablotron(PHPとかでも使える、何かの素粒子か加速器みたいな名前のXSLTプロセッサ)などでは次のような技巧が使えます:
<xsl:variable name="result"> <level>3</level> <number>1-1</number> </xsl:variable> <xsl:variable name="result-node" select="$result"/>
これで、$resultは結果ツリーフラグメントですが、$result-nodeは(select属性で値を与えているので)ノードセットになります。……って、こんなんでいいなら最初っからxsl:variableの内容でもノードセットにしろッ! と叫びたくなりますが、まぁXSLT 1.0の仕様書に「内容だったら結果ツリーフラグメントと見なせ」って書いてあるので、なんとも……。ちなみに独自拡張のnode-set関数でもいいようです。
IE 6でもこの技巧が使えたので、数年前からずっとこの方法を愛用しているのですが、どーもFirefoxのXSLTプロセッサはこれが駄目らしい。仕様書に書いてないからバグではないんだけど、悲しい。Opera 9はこの方法が使えるけど、document関数がまだ実装されてないし…(近々実装されるという噂だけはあるけども)。うーん。結局IE6以降でなければサーバーサイドで変換するしかないようで。はぁ、がっくし……