🤖

[XPath 2.0][XSLT 3.0]コンテキストアイテム(ノード)構造上の位置を表示しよう fn:trace()とその限界

に公開

https://adventar.org/calendars/11635
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(), '/') "/>

参考資料

https://www.w3.org/2006/xpath-functions/

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

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

Discussion