スタイルシート作成者による任意のノード集合を変数にバインドしたい場合があります。しかし一つのxsl:variable要素だけでは不可能です。二つのxsl:variable要素を使ってこれを無理矢理やってしまおうという話なので一見地味ですが、便利かも知れない応用が幾つかあります。
次のxsl:variable要素がトップレベル(ルート要素xsl:stylesheetの子供)にあるとします:
<xsl:variable name="test.fragment">
<li n="1" />
<li n="2" />
<li n="3" />
</xsl:variable>
$test.fragmentとして参照できるオブジェクトの型はノード集合ではありません。従って、<xsl:for-each select="$test.fragment" />
等とするとエラーになります。これは結果ツリーフラグメント(Result Tree Fragment)と呼ばれるXSLTの特殊な型です。string型として評価可能かどうかのチェックが行われ、しかし評価する際にはノード集合として扱われる、と考えて差し支えありません。つまり<xsl:for-each select="$test.fragment" />
は、<xsl:for-each select="'文字列'" />
と同じ理由で弾かれることになるのです。
仮に私がXSLTの試験問題を作るとしたら絶対このネタを使うだろう、という程に重要かつ誤解しやすいポイントなので要チェックです(11.1 Result Tree Fragments(XSL Transformations (XSLT)))。
この「結果ツリーフラグメント」を「ノード集合」と等価に扱うにはどうするか、というのがこの記事の要点です。
document関数を利用し、スタイルシート自身を経由したロケーションパスで表現することで解決します。
<xsl:variable
name="test.nodeset"
select="document('')/*/xsl:variable[@name = 'test.fragment']" />
ここで、document('')
は(殆どの場合)スタイルシート自身のルートノード一つを含んだノード集合です。相対URI('')の解決は、第二引数がない場合スタイルシート自身のURIとなるからです。正確にはdocument()関数を含むスタイルシートのノードのBase URIが基底になるので、そのノードが外部実体の場合はどうなのでしょうか。面倒くさいし話題が逸れまくるので考えないことにします。
ともかくこれで変換元文書(ソース文書)に依存しない、スタイルシート編集者による任意のノード集合を変数を通じて扱うことが出来ます。
T/Oとしたいところですが、例えば汎用的なスタイルシートを公開する際、XMLの構造そのものをユーザー(クライアント)が定義、という形式が可能です。具体例は面倒なので省略。
何回繰り返し処理する、という場合にはxsl:call-templateの再帰で行うのが通例ですが、例えば先に示した変数を使用すると、三回繰り返す場合には次のように書けます:
<xsl:for-each select="$test.nodeset/*">
(処理内容)
</xsl:for-each>
見た目がシンプルになる場合がある、というだけであまり推奨したくはありません。
結果ツリーフラグメントを入れるxsl:variable要素の方をカラッポにしておいて、プロセッサクラスにDOMノードツリーをそこに挿入するメソッドでも追加すれば、構造化されたパラメタを渡すことが出来ます。以前書いたXSLTをテンプレートとして使用する (agenda)の方法より汎用的です。