xsl:value-ofは値を返さない
xsl:value-of
は値を返す?
XSLTのxsl:value-of
は「値を返す」ものだと思ってないでしょうか?
(ここでは、「値」とはatomic valueのこととします。つまり、数値や文字列のような、個性のないデータのこととします。)
おそらくxsl:value-ofという名前に引きずられた理解なのでしょう。次のXSLTとCのコードを、ほぼ等価のように考えている人が多いようです。
XSLT
<xsl:function name="my:increment">
<xsl:param name="n" />
<xsl:value-of select="$n + 1" />
</xsl:function>
C
int myIncrement(
int n
)
{
return n + 1;
}
値を返しているようにも見える
たしかに、xsl:value-of
は値を返しているようにも見えます。
たとえば次のサンプルXSLTを実行すると、2 + 1
の結果、3
と表示されます。
xsl:value-ofで数値を返しているつもりの例
<xsl:stylesheet version="3.0"
xmlns:my="http://www.example.com/my"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="true" />
<!-- 数値を返しているつもり -->
<xsl:function name="my:increment">
<xsl:param name="n" />
<xsl:value-of select="$n + 1" />
</xsl:function>
<xsl:template name="xsl:initial-template">
<xsl:message select="my:increment(2)" />
</xsl:template>
</xsl:stylesheet>
実行結果
PS C:\value-of> java -jar saxon-he-10.5.jar -it -xsl:sample-1.xsl
3
xsl:value-of
は値を返さない
実際は、xsl:value-of
は値を返しません。返すのは、数値(xs:integer
)でもないし、文字列(xs:string
)でもないし、そもそもatomic valueですらありません。
それは、次のサンプルで確認できます。
xsl:value-ofの返す「値」の種類を確認する例
<xsl:stylesheet version="3.0"
xmlns:my="http://www.example.com/my"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="true" />
<!-- 数値を返しているつもり -->
<xsl:function name="my:increment">
<xsl:param name="n" />
<xsl:value-of select="$n + 1" />
</xsl:function>
<xsl:template name="xsl:initial-template">
<xsl:message select="'integer?', my:increment(2) instance of xs:integer" />
<xsl:message select="'string?', my:increment(2) instance of xs:string" />
<xsl:message select="'atomic?', my:increment(2) instance of xs:anyAtomicType" />
</xsl:template>
</xsl:stylesheet>
実行結果
PS C:\value-of> java -jar saxon-he-10.5.jar -it -xsl:sample-2.xsl
integer? false
string? false
atomic? false
xsl:value-of
はテキスト ノードを新規作成する
xsl:value-of
は値を返すための命令ではなく、テキスト ノードを新規作成する命令です。
確認してみましょう。
xsl:value-ofがテキスト ノードの新規作成であることを確認する例
<xsl:stylesheet version="3.0"
xmlns:my="http://www.example.com/my"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="true" />
<!-- 数値を返しているつもり -->
<xsl:function name="my:increment">
<xsl:param name="n" />
<xsl:value-of select="$n + 1" />
</xsl:function>
<xsl:template name="xsl:initial-template">
<xsl:message select="'text?', my:increment(2) instance of text()" />
<xsl:message select="'id', my:increment(2) => generate-id()" />
<xsl:message select="'id', my:increment(2) => generate-id()" />
<xsl:message select="'id', my:increment(2) => generate-id()" />
</xsl:template>
</xsl:stylesheet>
実行結果
PS C:\value-of> java -jar saxon-he-10.5.jar -it -xsl:sample-3.xsl
text? true
id Q1227264471
id Q1733581655
id Q1814827909
つまり、
<xsl:value-of select="$n + 1" />
は、
<xsl:text expand-text="yes">{$n + 1}</xsl:text>
と同じです。$n + 1
の結果の値を返すのではありません。$n + 1
の結果をもとに、毎回、新たな個性を持ったテキスト ノードを作成しているのです。
なお、今回の例で$n + 1
の結果を数値(xs:integer
)のまま返すには、xsl:value-of
ではなくxsl:sequence
を使います。
<xsl:sequence select="$n + 1" />
@as
を使おう
xsl:value-of
に限らず、こうした型にまつわる勘違いを避けるには、まずは@as
の指定を徹底することです。
たとえば、最初のサンプルXSLTのxsl:function
にas="xs:integer"
を付ければ、プロセッサーがWarningを出してくれます。(Saxonの場合)
xsl:functionにasを付けた例
<xsl:stylesheet version="3.0"
xmlns:my="http://www.example.com/my"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="true" />
<!-- 整数を受け、整数を返す…つもりなので、as="xs:integer"を付けた -->
<xsl:function name="my:increment"
as="xs:integer">
<xsl:param name="n"
as="xs:integer" />
<xsl:value-of select="$n + 1" />
</xsl:function>
<!-- 何も返さないテンプレートなので、as="empty-sequence()"を付けた -->
<xsl:template name="xsl:initial-template"
as="empty-sequence()">
<xsl:message select="my:increment(2)" />
</xsl:template>
</xsl:stylesheet>
実行結果
PS C:\value-of> java -jar saxon-he-10.5.jar -it -xsl:sample-5.xsl
Warning at function my:increment on line 10 of sample-5.xsl:
SXWN9000 A function that computes atomic values should use xsl:sequence rather than xsl:value-of
3
@as
を指定すれば、xsl:template
、xsl:function
、xsl:variable
などの意図が明確になるので、コード レビューも楽になります。
Discussion