XSLT3.0からはXSLTスタイルシートでも、一般のプログラミング言語でいうところのtry、catchが使えるようになります.これはXSLT2.0で変数やパラメータの型定義が導入され、テンプレートのパラメータの必須有無、またテンプレートの戻り値も型定義できるようになったことによるものでしょう.
実際XSLT1.0スタイルシート/プロセッサを使っている限り、下手をすると無限ループしたりスタックオーバーフローになることはありましたが、そこそこなんとか動いてくれてしまったのです.実際XSLT1.0は本当に「寛容」な仕様でした.
ところがXSLT2.0のスタイルシートを書くようになるとXSLTプロセッサはランタイムエラー(動的エラー)を容赦なく報告するようになります.私もお客さんのスタイルシートをXSLT1.0からXSLT2.0に書き換えましたが、ランタイムエラーの障害報告は恥ずかしながら増えました.
基本的にランタイムエラーのほとんどはコーディングミスに起因するものでしたので、そのときはtry,catchの必要性はあまり感じませんでした.しかしパラメータを外部からユーザーに指定してもらうような場合も想定されます.XSLT3.0でのtry,catchの導入はXSLT2.0の発展として時宜を得たものでしょう.
まずXSLT3.0でのtry,catchの文法は以下に詳述されています.
8.3 Try/Catch
http://www.w3.org/TR/xslt-21/#element-try
http://www.w3.org/TR/xslt-21/#element-try
例を示します.パラメータの値を整数値化して出力します.整数化できない場合はエラーメッセージを出して"0"を代わりに出力します.
[style.xml]
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:err="http://www.w3.org/2005/xqt-errors"
exclude-result-prefixes="xs err">
<xsl:output indent="yes"/>
<xsl:param name="prmVal" as="xs:string"/>
<xsl:template match="/">
<val>
<xsl:try select="xs:integer($prmVal)">
<xsl:catch>
<xsl:message select="'Error code:', $err:code, 'Reason:', $err:description"/>
<xsl:sequence select="'0'"/>
</xsl:catch>
</xsl:try>
</val>
</xsl:template>
</xsl:stylesheet>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:err="http://www.w3.org/2005/xqt-errors"
exclude-result-prefixes="xs err">
<xsl:output indent="yes"/>
<xsl:param name="prmVal" as="xs:string"/>
<xsl:template match="/">
<val>
<xsl:try select="xs:integer($prmVal)">
<xsl:catch>
<xsl:message select="'Error code:', $err:code, 'Reason:', $err:description"/>
<xsl:sequence select="'0'"/>
</xsl:catch>
</xsl:try>
</val>
</xsl:template>
</xsl:stylesheet>
これをSaxon9.3で以下のコマンドラインで呼び出すと次の出力を得ます.
[コンソール出力]
Error code: err:FORG0001 Reason: Cannot convert string "2e3" to an integer
Error code: err:FORG0001 Reason: Cannot convert string "2e3" to an integer
xsl:tryもxsl:catchもselect属性と、下位のsequence consutructorは排他的です.どちらかしか記述できません.xsl:tryはselect属性か下位のsequence consutructorを評価し、エラーがないときは「その値」を、エラーが発生した場合は「対応するcatchのselect属性かsequence constructorの値」を返してくれます.
ところでSaxon9.3のインプリメントにはまだバグがあるようで、次のようにxsl:eveluateでXPathの動的エラーを発生させてみようと思いましたが、NPE(Null Pointer Exception)でこけてしまいました.こんどSaxonのMLで報告しておこうと思います.
# このスタイルシートには誤りがあります.「XSLT3.0への道(3) 訂正 xsl:evaluate 」を参照ください.(2011/02/26)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:err="http://www.w3.org/2005/xqt-errors"
exclude-result-prefixes="xs"
>
<xsl:output indent="yes"/>
<xsl:param name="prmXPathFile" as="xs:string" select="'xpath.xml'"/>
<xsl:template match="/">
<output>
<xsl:for-each select="doc($prmXPathFile)//xpath">
<xsl:variable name="xPath" select="string(.)" as="xs:string"/>
<xsl:message select="'XPath=',$xPath"/>
<xsl:try>
<xsl:evaluate xpath="string(.)"/>
<xsl:catch>
<xsl:message terminate="yes" select="'XPath evaluation failed: Error code:', $err:code, 'Reason:', $err:description"/>
</xsl:catch>
</xsl:try>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:err="http://www.w3.org/2005/xqt-errors"
exclude-result-prefixes="xs"
>
<xsl:output indent="yes"/>
<xsl:param name="prmXPathFile" as="xs:string" select="'xpath.xml'"/>
<xsl:template match="/">
<output>
<xsl:for-each select="doc($prmXPathFile)//xpath">
<xsl:variable name="xPath" select="string(.)" as="xs:string"/>
<xsl:message select="'XPath=',$xPath"/>
<xsl:try>
<xsl:evaluate xpath="string(.)"/>
<xsl:catch>
<xsl:message terminate="yes" select="'XPath evaluation failed: Error code:', $err:code, 'Reason:', $err:description"/>
</xsl:catch>
</xsl:try>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
xsl:try, xsl:catchが使用できるようになれば、スタイルシートのエラー処理も今より自然な形でできるようになると期待できます.何より今までは動的エラーをトラップする手段がなかったのですから、やはり必要な機能ですね.