🤘

[XPath 2.0][XSLT 3.0]君だけのエラーをでっち上げろ! fn:error()

に公開

https://adventar.org/calendars/11635
XSLT 3.0デバッグまわり Advent Calendar 2025 の4日目。

2025-12-09 追記。XPathとしてはerror()は2.0からあるので改題しました。

動的エラーのハンドリングがしたい

XSLT 3.0ではエラーハンドリングが向上しています。
2.0で大きく進んだType Error系も1.0のエラー処理がゆるかったので大きな進歩ですが、
Dynamic Error系は課題がかなり残っていたのでした。

そんなDynamic Error処理の中心はxsl:tryとxsl:catchです。 ですが、これは別の機会に譲るとして**dynamicなエラーは自分でもっと分かり易いラベルを付けたい!** これに応えるのがfn:error()`となります。XSLTというよりXPathですが、XSLTで使うにはちょっと面倒というところを書くのでXSLT 3.0の話です。

fn:error()製じゃない独自エラーについて

拡張関数や機能を実装して組み込んでいる場合、処理失敗時のエラーはやはり独自で作りこむことになるでしょう。その場合は実装側のプログラム内でXSLTに露出させるエラー部分も作ることになるでしょう。
implementation-dependentということで、今回の範囲外。

xsl:messagexsl:assertでは@error-codeでエラーコードをいじれますが、これはエラーコードだけ変わります。
自分で独自のエラーコードを名乗れる一方、それがどんなエラーであるかなど、細かい事は設定できません。
(使い道を思いついていないのであれですが)fn:error()$error-objectに相当するものを持っておきたいときには不足することになるでしょう。

fn:error()

fn:error()の引数は0-4つあります。
いずれも返り値はなく、代わりにErrorを上げます(Raise Error)。
0のときは何のエラーなのかまったく情報が無く虚無になってしまうため、止めましょう。
xsl:try/xsl:catchでハンドリングしない場合、エラーが出れば基本的に変換処理は失敗となります。

  1. エラーコードのQName
  2. エラーコードのQName, エラーの説明(description)
  3. エラーコードのQName, エラーの説明(description), エラーオブジェクト(item()*)

QName型を得る方法はややこしい点がありまして、fn:QName()xs:QName()は引数が違います。
前者はNamepsace URIとlocal-nameのstring、後者はprefix付きのnameのstringが必要となります。
まあ、xsl:catchで使ったりするなら事前に同じnamespaceを宣言して使うことになるので、XSLTではxs:QName()の方が使い易いでしょう。

<xsl:sequence xmlns:my="urn:my"
  select="xs:QName('my:tinyErr') => fn:error()"/>

$descriptionはエラーメッセージです。
実際のところ、エラーが生じたときにエラーコードが示されるとは限らないため[1]、エラーコードもstringとして一緒に表示されるようにしておいた方が手堅いでしょう。

<xsl:sequence xmlns:my="urn:my"
  select="let $codeString := 'my:tinyErr',
              $description := $codeString || 'というエラーは……' 
          return 
              xs:QName($codeString) => fn:error( $decription )"/>

$error-objectは追加の情報となるのですが、割とどうしたらいいか分からない箇所です。
おそらくxsl:catch$err:valueを取ろうとすると$error-objectが渡されることになると思うのですが、テキストとして表示するだけで$descriptionでやればいいので、活用方法をあまり思いつけていません。

xsl:functionでラップしておく

毎回fn:error()の中身やQNameを書くのは大変ですので、ラップしておくと便利です。

<xsl:stylesheet 
  xmlns:my="urn:my" ...>
  <xsl:variable name="myErrCodeQName" as="xs:QName" select="xs:QName('my:err')"/>
  <xsl:function name="my:wrapErr" >
  <xsl:sequence 
      select="let $description := string($myErrCodeQName) || 'というエラーは……' 
              return $myErrCodeQName => fn:error( $decription )"/>
  </xsl:function>

  <xsl:template ...>
    ...
    <xsl:sequence select="if (...) then (...) else (my:wrapErr())"/>
  </xsl:template>
</xsl:stylesheet>

エラー箇所の情報が欲しいし、実際にはfunctionにcontext-itemをparamで要求する形になることが多いでしょう。

問題点

エラー型を定義しておいてどこからでも使うという用途には使えません。
XQueryでは分かりませんが、XSLTではあるXPath式内を超えて独自定義したエラー型を使い回すのではなく、都度fn:error()で作成したフレッシュなエラーを使うことになるでしょう。

参考資料

https://www.w3.org/TR/xpath-functions-31/#func-error

脚注
  1. xsl:catchの中でなら$err:codeで取れるが。 ↩︎

組版・ドキュメンテーション勉強会

Discussion