XSLT1.0からXSLT2.0へスタイルシートを移行することがありますが、これらの演算子はついついXSLT1.0で使用した一般比較演算子のまま残してしまいます.私はなるべく値比較演算子に書き換えるようににしています.
理由ですが、本来スタイルシートで行うほとんどの比較は値比較のはずだからです.一般比較演算子をつかう必要があるのはのは、相当意識的にやる場合です.一般比較で右辺・左辺どちらかの式がシーケンスになってしまう場合、値比較演算子の場合エラーをだしてくれます.ノードを参照している場合ついついこういうことが起こりがちだからです.例えば
<xsl:if test="100 > /root/data/@value">
ような場合、count(/root/data/) eq 1 ならばなんの問題もありませんが、間違えてcount(/root/data/) gt 1 の場合、すべての/root/dataに@valueがあれば、整数値1とシーケンスの比較になってしまいます.
<xsl:if test="100 gt /root/data/@value">
なら値比較演算子の右辺がシーケンスであるとエラーを出してくれるのですぐバグがわかります.
XPTY0004: A sequence of more than one item is not allowed as the second operand of 'gt'
Transformation failed: Run-time errors were reported
Transformation failed: Run-time errors were reported
あまりご利益はないでしょうか?
[入力XML]
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<data>65536</data>
<data>1254.25</data>
<data>2.5e-10</data>
<data>Monday</data>
<data> </data>
</root>
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<data>65536</data>
<data>1254.25</data>
<data>2.5e-10</data>
<data>Monday</data>
<data> </data>
</root>
[スタイルシート]
<xsl:variable name="data" select="/root/data" as="node()*"/>
<xsl:if test="65536 = $data">
<xsl:message select="'65536 = $data'"/>
</xsl:if>
<xsl:if test="1254.25 = $data">
<xsl:message select="'1254.25 = $data'"/>
</xsl:if>
<xsl:if test="2.5e-10 = $data">
<xsl:message select="'2.5e-10 = $data'"/>
</xsl:if>
<xsl:if test="'Monday' = $data">
<xsl:message select="'''Monday'' = $data'"/>
</xsl:if>
<xsl:if test="' ' = $data">
<xsl:message select="''' '' = $data'"/>
</xsl:if>
<xsl:variable name="data" select="/root/data" as="node()*"/>
<xsl:if test="65536 = $data">
<xsl:message select="'65536 = $data'"/>
</xsl:if>
<xsl:if test="1254.25 = $data">
<xsl:message select="'1254.25 = $data'"/>
</xsl:if>
<xsl:if test="2.5e-10 = $data">
<xsl:message select="'2.5e-10 = $data'"/>
</xsl:if>
<xsl:if test="'Monday' = $data">
<xsl:message select="'''Monday'' = $data'"/>
</xsl:if>
<xsl:if test="' ' = $data">
<xsl:message select="''' '' = $data'"/>
</xsl:if>
結果は順当に以下のように表示されます.(幸せな世界です)
65536 = $data
1254.25 = $data
2.5e-10 = $data
'Monday' = $data
' ' = $data
1254.25 = $data
2.5e-10 = $data
'Monday' = $data
' ' = $data
ところが入力XMLの先頭行を次のように変えると一発でエラーになります.
<root>
<data>Sunday</data>
<data>65536</data>
...
FORG0001: ValidationException: Cannot convert string "Sunday" to a double
Transformation failed: Run-time errors were reported
<data>Sunday</data>
<data>65536</data>
...
FORG0001: ValidationException: Cannot convert string "Sunday" to a double
Transformation failed: Run-time errors were reported
"Sunday"はxs:doubleに変換できないというエラーです.これはatomic valueとnode()*との比較では、atomic valueにあわせてnode()*の値が変換して比較されるからです.Saxonの場合、右辺のシーケンスの$dataはシーケンスのメンバーが順に変換されてゆくようです.つまり$dataのメンバーを全部左辺のatomic valueに変換してから比較をするのではない.最初のXMLでRuntime errorが出なかったのは、比較順がうまくエラーがでないようになっていただけのことでした.
[スタイルシート]
<xsl:if test="$data[2] = $data">
<xsl:message select="string($data[2]), '= $data'"/>
</xsl:if>
<xsl:if test="$data[3] = $data">
<xsl:message select="string($data[3]), '= $data'"/>
</xsl:if>
<xsl:if test="$data[4] = $data">
<xsl:message select="string($data[4]),'= $data'"/>
</xsl:if>
<xsl:if test="$data[5] = $data">
<xsl:message select="string($data[5]),' = $data'"/>
</xsl:if>
<xsl:if test="$data[6] = $data">
<xsl:message select="concat('''',string($data[6]),''' = $data')"/>
</xsl:if>
<xsl:if test="$data[2] = $data">
<xsl:message select="string($data[2]), '= $data'"/>
</xsl:if>
<xsl:if test="$data[3] = $data">
<xsl:message select="string($data[3]), '= $data'"/>
</xsl:if>
<xsl:if test="$data[4] = $data">
<xsl:message select="string($data[4]),'= $data'"/>
</xsl:if>
<xsl:if test="$data[5] = $data">
<xsl:message select="string($data[5]),' = $data'"/>
</xsl:if>
<xsl:if test="$data[6] = $data">
<xsl:message select="concat('''',string($data[6]),''' = $data')"/>
</xsl:if>
すると、さっきはエラーが出たのに今度は以下のように表示されます.
65536 = $data
1254.25 = $data
2.5e-10 = $data
Monday = $data
' ' = $data
1254.25 = $data
2.5e-10 = $data
Monday = $data
' ' = $data
何故エラーが出ないのでしょう?今度はnodeとnode()*との比較になります.この場合、XML Schemaでデータの型がマッピングされていない限り、xs:stringとxs:string*の比較になります.ですから決してRuntime errorは発生せず、かつ必ずヒットすることになります.