🤖
[XPath 2.0][XSLT 3.0]コンテキストアイテム(ノード)構造上の位置を表示しよう fn:trace()とその限界
XSLT 3.0デバッグまわり Advent Calendar 2025 9日目わよ。
fn:trace()
「表示結果や処理結果がおかしいけれど、入力のどの部分を処理したのかいまいち分からない」 こういう事態は割と起こります。 基本的にはtransoformは元の構造を潰す方向で行うためであるのと、 基本的にmatchテンプレートでlocation step指定に階層位置を省きがちなためです。 ex. html:p→fo:block, html:h2 -> fo:blockみたいな感じで。
ex match="p" であってmatch="/body/div/p | body/p"などのように一々構造位置を指定しない。
fn:trace()は、現在のコンテキストノードについて、構造をトレースして表示してくれます。……ログ出力の方に。ちなみに返り値は入力をそのまま返すぞ!
入力がこれだとする。
<doc>
<title>aa</title>
<section>
<title>bb</title>
<section>
<title>ccc</title>
</section>
<section>
<title>ddd</title>
</section>
</section>
</doc>
で、コメントにtrace()を使うとする。
<xsl:template match="title">
<xsl:comment select="fn:trace(.)"/>
<xsl:copy><xsl:apply-templates/></xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:comment select="fn:trace(.)"/>
...
</xsl:template>
結果はたとえば次のようになる。
<!-aa-->
<title>aa</title>
match="child::title"のコンテキストノードはtext()「aa」となるという。一方ログ側は
* [1]: element(doc, xs:untyped): /doc
* [1]: element(title, xs:untyped): /doc/title[1]
* [1]: element(section, xs:untyped): /doc/section[1]
* [1]: element(title, xs:untyped): /doc/section[1]/title[1]
* [1]: element(section, xs:untyped): /doc/section[1]/section[1]
* [1]: element(title, xs:untyped): /doc/section[1]/section[1]/title[1]
* [1]: element(section, xs:untyped): /doc/section[1]/section[2]
* [1]: element(title, xs:untyped): /doc/section[1]/section[2]/title[1]
わあい。
ちなみに、左側の部分はカスタムしたラベルにできます。
<xsl:comment select="fn:trace(., 'Trace on ')"/>
Trace on [1]: element(doc, xs:untyped): /doc
Trace on [1]: element(title, xs:untyped): /doc/title[1]
Trace on [1]: element(section, xs:untyped): /doc/section[1]
Trace on [1]: element(title, xs:untyped): /doc/section[1]/title[1]
Trace on [1]: element(section, xs:untyped): /doc/section[1]/section[1]
Trace on [1]: element(title, xs:untyped): /doc/section[1]/section[1]/title[1]
Trace on [1]: element(section, xs:untyped): /doc/section[1]/section[2]
Trace on [1]: element(title, xs:untyped): /doc/section[1]/section[2]/title[1]
出力に構造位置を出したい
ポジションは出ないけど、簡易的にはこういうので出せます。
<xsl:comment select="fn:string-join(ancestor-or-self::*/fn:name(), '/') "/>
参考資料
Discussion