[XSLT 1.0]XSLT 1.0で可能なデバッグまわり
XSLT 3.0デバッグまわり Advent Calendar 2025の1日目の記事です。
アドベントカレンダーとしては2.0,3.0で可能になった機能を重点的に扱いますが、1.0時代からできることをこの記事で扱います。
<xsl:message>
処理中にメッセージを表示します。
標準出力に出すので、「元構造のこれがこの処理に対応します」といったログを書くには一工夫が必要になります。
<xsl:template match="p">
<!-- 次の使い方ではたとえば「pにmatchして処理した」くらいしかわからない -->
<xsl:message><xsl:value-of select="local-name(.)"/></xsl:message>
<xsl:apply-templates/>
</xsl:template>
@terminate="yes"を付けると、そこで処理が終了します。
<xsl:template match="p">
<xsl:if test="*/@requied">
<xsl:message terminate="yes">pに必須属性requiredがありません。処理を停止します</xsl:message>
</xsl:if>
...
</xsl:template>
<xsl:comment>
出力(xsl:output/@method)がXML・HTML形式のとき、コメントノードとして出力ファイルに情報を残すことができます。
<xsl:template match="p">
<xsl:comment>p</xsl:comment>
<fo:block><xsl:apply-templates/></fo:block>
</xsl:template>
<xsl:comment>と<xsl:message>の使い分けについて
XSLTの場合、基本的には変換後の文書が存在します。そのため、「変換後の文書におかしいところがあるので調査したい」というケースでは<xsl:comment>に変換前の要素や構造の情報を記載する方が良い場合が多いです。
とはいえ、xsl:output@method="text"のときはXMLのコメント書式は役に立ちません。
また、処理実行時のどこかでエラーが生じ、出力文書を得られないケースもあります。
その辺りのケースも踏まえた上で、「入力と出力の対応に関して問題がある、想定通りでない」問題を調査するときは、xsl:commentがよいでしょう。
では<xsl:message>はというとそれ以外の全てになります。まあprintfデバッグみたいなものだしね。
想定外の要素が来たときにマッチする優先度の低いテンプレートを作る
すべての構造について個別のmatchテンプレートを用意することは難しいことです。
とはいえ、文書に対しそのスキーマが存在する場合、想定し得る構造を網羅するというというのは多少現実的な話となります。
XSLT 1.0では、「想定されていない要素・テキストノードが来たとき、とりあえずテキストを出力する」ということになっています[1]。
困りがちなケースとしては、索引用のマークアップなどメタデータ用の構造で、直接その位置で出力されると嬉しくない要素があると後から発覚した、などでしょうか。
テンプレートには優先度があります。
基本的に5.5 Conflict Resolution for Template Rulesに従うことになりますが、暗黙のうちに従うには少々複雑です。
よって、
-
xsl:importで読み込んだルールの優先度が低い
を覚えておいた上で、
- 同じスタイルシートファイルと
xsl:includeで読み込まれたファイル内のテンプレートでは@priorityで優先度を明示的に指定して管理する
あたりであれば、何とか管理できるかと思います。
そして、ワイルドカードで優先度が低いmatchテンプレートを用意し、その処理を決めておくことで、処理実行時に気付けるようにしておきます。
<xsl:template match="p" mode="main" priority="3">
<fo:block>
<xsl:apply-templates mode="main"/>
</fo:block>
</xsl:template>
<xsl:template match="*" priority="1" mode="main">
<!-- 明示的に用意していない構造のfallback -->
<xsl:message >
<xsl:value-of select="concat('NOT EXPECTED STRUCTURE' , name())"/>
</xsl:message>
<xsl:apply-templates mode="main"/>
</xsl:template>
デバッグテキストのローカライズ
https://www.w3.org/TR/xslt-10/#messageにローカライズのtipがあります。
xsl:variable内でエラーメッセージのXMLファイルを呼び出し、状況に応じて差し換えるというものです。
xsl:messageやxsl:commentの中もXSLTのルールで処理できる故の手法ですが、自前でこの辺りを各言語分用意する程の規模のプロジェクトもそうそう無いでしょう。
<!-- localizeメッセージの作例 -->
<xsl:variable name="messages">
<messages>
<message id="err1">
<locale xml:lang="ja">これは...</locale>
<locale xml:lang="en">This is ...</locale>
</message>
</messages>
</xsl:variable>
<xsl:template ...>
<xsl:message ><xsl:value-of select="$messages//message[@id = 'err1']/*[lang('en')]"></xsl:message>
</xsl:template>
その他
「空ノードと空文字列の区別が必要な状況を極力避ける」など、デバッグ以前の「原因:うっかりした、対処:しっかりする」くらいになりがちなのがXSLT 1.0です。
ブラウザのサポートからドロップされるという話もありますし、つい先日リリースされたOxygen 28.0でも1.0処理系であるmsxslがバンドルから削除されています。
XSLTを使う必要があるとき、第一歩として「システムがXSLT 2.0以降を使えるようにする」ことを検討してください。
参考文献
XSLT3.0への道(32) 使ってみた感想 tmakitaのブログ https://toshi-xt500.hatenablog.com/entry/18111740
-
XSLT 3.0では
xsl:mode/@on-no-match="text-only-copy"に相当します ↩︎
Discussion