XSLT2.0でシーケンスが導入されて大変便利になりました.変数をxs:integer*、xs:string+などと定義できるとプログラミングの自由度が格段に向上します.さてそれではnode()*はどうでしょうか?これもまた便利なのですが、document-node()とどう違うのか?たまに混乱することがあります.実は先週node()*で定義していた変数がどうしてもうまく動いてくれなくて、結果的にdocument-node()が正解であることに気がつき、スタイルシートを全部修正しました.これに懲りたので、今回両者の違いについてまとめてみたいと思います.
まずdocument-nodeですが、変数の下にリテラルで要素を並べるとこのタイプになります.
<!--例1-->
<xsl:variable name="div">
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</xsl:variable>
<xsl:variable name="div">
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</xsl:variable>
<xsl:template match="/">
<xsl:message select="'$div/span[1]=',$div/span[1]"/>
</xsl:template>
<xsl:message select="'$div/span[1]=',$div/span[1]"/>
</xsl:template>
出力は
$div/span[1]=<span>りんご</span>
これは次のように書いたことと同じになります.
<!--例2-->
<xsl:variable name="div" as="document-node()">
<xsl:document>
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</xsl:document>
</xsl:variable>
<xsl:variable name="div" as="document-node()">
<xsl:document>
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</xsl:document>
</xsl:variable>
では、
<!--例3-->
<xsl:variable name="div" as="node()*">
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</xsl:variable>
<xsl:variable name="div" as="node()*">
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</xsl:variable>
と書いたらどうなるのでしょう?
$div/span[1]=
あれっ?何も出てきません.これはつまり、node()*に"/"の記法を適用しようとしてもダメなことを意味しています.この場合、代わりに、
<xsl:message select="'$div[1]=',$div[1]"/>
と書くと
$div[1]=<span>りんご</span>
と出てきてくれます.
あと重要なのがノードの相互関係です.例1と2では、
<xsl:message select="'$div/span[1]/following-sibling::node()[1]=',$div/span[1]/following-sibling::node()[1]"/>
は
$div/span[1]/following-sibling::node()[1]=<span>みかん</span>
で期待通りです.しかし例3で
<xsl:message select="'$div[1]/following-sibling::node()[1]=',$div[1]/following-sibling::node()[1]"/>
とやっても
$div[1]/following-sibling::node()[1]=
と何もでてきてくれません.そうです、次のように書くと何かしらツリーを作ってくれるように見えますが、
<!--例3-->
<xsl:variable name="div" as="node()*">
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</xsl:variable>
<xsl:variable name="div" as="node()*">
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</xsl:variable>
これがnode()*として扱われると<span>りんご</span>、<span>みかん</span>、<span>バナナ</span>が各々div[1]、div[2]、div[3]になるだけで要素の兄弟関係はなくなるのです.
しかし入力XMLが
<?xml version="1.0" encoding="UTF-8" ?>
<div>
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</div>
<div>
<span>りんご</span>
<span>みかん</span>
<span>バナナ</span>
</div>
となっていて、
<xsl:variable name="div" as="node()*">
<xsl:sequence select="/div/span[1]"/>
</xsl:variable>
<xsl:sequence select="/div/span[1]"/>
</xsl:variable>
とやれば、
<xsl:message select="'$div[1]/following-sibling::*[1]=',$div[1]/following-sibling::*[1]"/>
$div[1]/following-sibling::*[1]=<span>みかん</span>
と出ます.つまりソースXMLのノードを持って来れば、ノードの兄弟関係は保持されるのです.
ここで<xsl:sequence select="/div/span[1]"/>と、元の要素(ノード)を持ってきている点に注意してください.
<xsl:copy-of select="/div/span[1]"/>
とやると、要素がコピーされて新しい要素が作られるので、元の兄弟間の関係は保持されません.
あとnode()*の変数$aは、empty() eq true()になりえます.
<xsl:variable name="div" as="node()*">
</xsl:variable>
</xsl:variable>
しかし、
<xsl:variable name="div" as="document-node()">
<xsl:document/>
</xsl:variable>
<xsl:document/>
</xsl:variable>
は必ずexists($div) eq trueになります.(あたりまえですが...)
例えば次のようにテンポラリツリーを作って
<xsl:variable name="div" as="document-node()">
<xsl:document>
<xsl:apply-templates select="/div/span"/>
</xsl:document>
</xsl:variable>
<xsl:document>
<xsl:apply-templates select="/div/span"/>
</xsl:document>
</xsl:variable>
有効な要素(ノード)ができているかは
exists($div/*)
で確認できます.
node()*もdocument-node()も、複雑なデータ構造を扱うXSLTプログラミングでは必須になります.お使いになる場合、上記に述べた点に注意していただければと思います.