You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

XSLT问题:基于目录元数据的递归模板未达预期效果

基于目录元数据生成层级唯一标识符的XSLT解决方案

我正尝试从目录元数据生成唯一标识符,用于后续文本数据库的使用。我编写了一个使用<xsl:for-each>的递归调用模板,但未得到预期结果。


源文档

<html lang="en">
    <head>
        <title>Title</title>
    </head>
    <body>
        <h1 data-toc="1" data-stub="Title1">Title 1—General</h1>
        <h2 data-toc="5" data-stub="Part1">PART 1—DEFINITIONS</h2>
        <h3 data-toc="9" data-stub="Sec1">§ 1  Definitions.</h3>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec1.1">§ 1.1  Foo.</h3>
        <p>foo</p>
        <p>foo</p>
        <h2 data-toc="5" data-stub="Part2">PART 2—THE COMMITTEE</h2>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec2">§ 2  Definitions.</h3>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec2.1">§ 2.1  Foo.</h3>
        <p>foo</p>
        <p>foo</p>
        <h2 data-toc="5" data-stub="Part3">PART 3—THE COUNCIL</h2>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec3">§ 3  Definitions.</h3>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec3.1">§ 3.1  Foo.</h3>
        <p>foo</p>
        <p>foo</p>
    </body>
</html>

现有样式表

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

    <xsl:output method="xhtml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[@data-toc]">
        
        <xsl:variable name="build-callcode">
            <xsl:call-template name="build-call-code">
                <xsl:with-param name="this-toc-level" select="@data-toc"/>
                <xsl:with-param name="this-stub" select="@data-stub"/>
            </xsl:call-template>
        </xsl:variable>

        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:attribute name="data-callcode" select="$build-callcode"/>
            <xsl:apply-templates/>
        </xsl:copy>

    </xsl:template>

    <xsl:template name="build-call-code">
        <xsl:param name="this-toc-level"/>
        <xsl:param name="this-stub"/>

        <xsl:for-each select="preceding::*[@data-toc &lt; $this-toc-level][1]">
            <xsl:call-template name="build-call-code">
                <xsl:with-param name="this-toc-level" select="@data-toc"/>
                <xsl:with-param name="this-stub" select="concat(@data-stub, $this-stub)"/>
            </xsl:call-template>
            <xsl:value-of select="concat(@data-stub, $this-stub)"/>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

期望结果

<html lang="en">
    <head>
        <title>Title</title>
    </head>
    <body>
        <h1 data-toc="1" data-stub="Title1" data-callcode="Title1">Title 1—General</h1>
        <h2 data-toc="5" data-stub="Part1" data-callcode="Title1Part1">PART 1—DEFINITIONS</h2>
        <h3 data-toc="9" data-stub="Sec1" data-callcode="Title1Part1Sec1">§ 1  Definitions.</h3>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec1.1" data-callcode="Title1Part1Sec1.1">§ 1.1  Foo.</h3>
        <p>foo</p>
        <p>foo</p>
        <h2 data-toc="5" data-stub="Part2" data-callcode="Title1Part2">PART 2—THE COMMITTEE</h2>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec2" data-callcode="Title1Part2Sec2">§ 2  Definitions.</h3>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec2.1" data-callcode="Title1Part2Sec2.1">§ 2.1  Foo.</h3>
        <p>foo</p>
        <p>foo</p>
        <h2 data-toc="5" data-stub="Part3" data-callcode="Title1Part3">PART 3—THE COUNCIL</h2>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec3" data-callcode="Title1Part3Sec3">§ 3  Definitions.</h3>
        <p>foo</p>
        <p>foo</p>
        <h3 data-toc="9" data-stub="Sec3.1" data-callcode="Title1Part3Sec3.1">§ 3.1  Foo.</h3>
        <p>foo</p>
        <p>foo</p>
    </body>
</html>

问题分析与修正方案

原递归模板的核心问题:

  1. 递归时提前拼接父节点与当前节点的stub,导致层级逻辑混乱
  2. 仅获取最近的单个父层级节点,未正确追溯所有上层级的祖先节点

修正后的样式表

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">

    <xsl:output method="xhtml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[@data-toc]">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:attribute name="data-callcode">
                <xsl:call-template name="build-call-code">
                    <xsl:with-param name="current-node" select="."/>
                </xsl:call-template>
            </xsl:attribute>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <xsl:template name="build-call-code">
        <xsl:param name="current-node"/>
        <!-- 获取当前节点之前最近的、层级更低的节点 -->
        <xsl:variable name="parent-node" select="$current-node/preceding::*[@data-toc &lt; $current-node/@data-toc][1]"/>
        <!-- 递归输出所有父层级节点的stub -->
        <xsl:if test="$parent-node">
            <xsl:call-template name="build-call-code">
                <xsl:with-param name="current-node" select="$parent-node"/>
            </xsl:call-template>
        </xsl:if>
        <!-- 输出当前节点的stub -->
        <xsl:value-of select="$current-node/@data-stub"/>
    </xsl:template>

</xsl:stylesheet>

修正逻辑说明

  1. 递归模板传递完整节点对象,避免拆分层级与stub导致的拼接错误
  2. 先递归追溯所有上层级的祖先节点,按层级顺序输出父节点的stub,最后输出当前节点的stub
  3. 顶层节点无父层级时,直接输出自身stub,符合预期的层级拼接规则

内容的提问来源于stack exchange,提问作者Paulb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 08:35:50