position()とコンテキストノードセット
position()
を使えばコンテキストノードリストの中のコンテキストノードの位置を得ることができます.position()
を使用する際には,「コンテキストノードリスト内の位置を返す」のであり,「テンプレートに合致したノードセット内の位置を返す」のではない点に注意が必要です.例として,以下のXMLとXSLTを考えます.
<!-- sam.xml --> <?xml version="1.0" encoding="utf-8"?> <root> <item>a</item> <item>b</item> <item>c</item> </root> <!-- sam.xsl --> <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="/root"> <xsl:apply-templates/> </xsl:template> <xsl:template match="item"> <xsl:value-of select="position()"/> <xsl:copy-of select="self::node()"/> </xsl:template> </xsl:stylesheet>
XTを使ってsam.xmlにsam.xslを適応すると,このような結果になりました.
$ xt sam.xml sam.xsl <?xml version="1.0" encoding="utf-8"?> 2<item>a</item> 4<item>b</item> 6<item>c</item>
1,2,3と番号を振ると思っていましたが,結果は2,4,6となりました.
これは,/rootに記述している<apply-templates/>
に原因があります.この<apply-templates/>
にはselect
属性の指定が何もないので,rootの子要素全てがコンテキストノードセットとなります.一見するとrootの子要素はitemしかないように見えますが,テキストノードとしての改行もコンテキストノードセットに含みます.つまり,
<!-- sam.xsl --> <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="/root"> <xsl:apply-templates/> <!-- ここでコンテキストノードセットを作る --> </xsl:template> <xsl:template match="item"> <!-- コンテキストノードセットの中のitem要素に合致 --> <xsl:value-of select="position()"/> <!-- コンテキストノードセットの中のコンテキストノードの位置を返す --> <xsl:copy-of select="self::node()"/> </xsl:template> </xsl:stylesheet> <!-- sam.xml --> <?xml version="1.0" encoding="utf-8"?> <root> <!-- rootのすぐ後の改行がコンテキストノードセットの1番目 --> <item>a</item> <!-- itemが2番目,itemの後ろの改行が3番目 --> <item>b</item> <!-- itemが4番目,itemの後ろの改行が5番目 --> <item>c</item> <!-- itemが6番目,itemの後ろの改行が7番目 --> </root>
となります.<apply-templates/>
で作成したコンテキストノードリストに<template match="item"/>
を適応するので,各itemのposition()
は2,4,6となるわけです.position()
は<apply-templates/>
で作成したコンテキストノードセット内の位置を取得するもので,<template match="item"/>
で抽出したノードセット内の位置を返すのではない点に注意してください.
改行以外のitem要素だけでコンテキストノードセットを作る場合は,<apply-templates/>
にselect
属性を指定します.
<!-- sam.xsl --> <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="/root"> <xsl:apply-templates select="item"/> <!-- item要素だけのコンテキストノードセットを作る --> </xsl:template> <xsl:template match="item"> <xsl:value-of select="position()"/> <xsl:copy-of select="self::node()"/> </xsl:template> </xsl:stylesheet> <!-- sam.xml --> <?xml version="1.0" encoding="utf-8"?> <root> <item>a</item> <item>b</item> <item>c</item> </root> $ xt sam.xml sam.xsl <?xml version="1.0" encoding="utf-8"?> 1<item>a</item>2<item>b</item>3<item>c</item>
ちなみに,sam.xmlに改行を入れず,以下のように変えると,select
属性を指定しない<apply-templates/>
を使っても1,2,3と番号を振ります.
<!-- sam.xml --> <?xml version="1.0" encoding="utf-8"?> <root><item>a</item><item>b</item><item>c</item></root> <!-- sam.xsl --> <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml"/> <xsl:template match="/root"> <xsl:apply-templates/> <!-- rootの子要素でコンテキストノードセットを作る.改行を入れていないので,itemだけ --> </xsl:template> <xsl:template match="item"> <xsl:value-of select="position()"/> <xsl:copy-of select="self::node()"/> </xsl:template> </xsl:stylesheet> $ xt sam.xml sam.xsl <?xml version="1.0" encoding="utf-8"?> 1<item>a</item>2<item>b</item>3<item>c</item>