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

XSL 3.0脚注编号与注文关联异常:处理顺序排查

解决XSLT 3.0双套编号脚注的编号显示问题

嘿,你猜的完全没错!问题根源就是XSLT的处理顺序:当你在生成footnote-block的时候,脚注的编号还没被计算出来呢——毕竟很多时候我们是在输出脚注节点时才用<xsl:number>或者计数器生成编号,而不是提前把编号绑定到节点上。这时候用.去取编号自然是空的,只有前置元素的@n能拿到。

新增mode确实是最靠谱的解决方案,核心思路是把「给脚注分配编号」和「生成脚注块内容」拆成两个独立的处理阶段,确保脚注编号在生成注块前就已经被计算并存储好。下面给你具体的实现思路和代码示例:

步骤1:用mode预处理脚注,分配并存储编号

我们先创建一个专门的mode(比如assign-footnote-numbers),遍历所有footnote和appnote节点,给它们分配对应的编号(数字/字母序列),并把编号存到临时属性里(比如@temp-num):

<!-- 预处理脚注,分配编号的模板 -->
<xsl:template match="footnote | appnote" mode="assign-footnote-numbers">
  <xsl:copy>
    <!-- 给footnote分配数字编号,appnote分配字母编号 -->
    <xsl:attribute name="temp-num">
      <xsl:choose>
        <xsl:when test="self::footnote">
          <xsl:number count="footnote" format="1"/>
        </xsl:when>
        <xsl:when test="self::appnote">
          <xsl:number count="appnote" format="a"/>
        </xsl:when>
      </xsl:choose>
    </xsl:attribute>
    <!-- 保留原节点的所有属性和子节点 -->
    <xsl:apply-templates select="@* | node()" mode="#current"/>
  </xsl:copy>
</xsl:template>

<!-- 默认模板,确保预处理时不丢内容 -->
<xsl:template match="@* | node()" mode="assign-footnote-numbers">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()" mode="#current"/>
  </xsl:copy>
</xsl:template>

步骤2:先完成编号预处理,再生成最终内容

在主模板里,我们先把整个文档用上面的mode预处理一遍,得到带编号属性的节点集合,然后再基于这个集合处理主内容和脚注块:

<xsl:template match="/">
  <!-- 第一步:预处理全文档,给脚注加上编号属性 -->
  <xsl:variable name="processed-content">
    <xsl:apply-templates select="." mode="assign-footnote-numbers"/>
  </xsl:variable>
  
  <!-- 第二步:基于预处理后的内容,生成最终输出 -->
  <xsl:apply-templates select="$processed-content"/>
</xsl:template>

步骤3:处理footnote-block,获取提前存储的编号

现在处理footnote-block的时候,就能直接引用前置脚注节点的@temp-num属性了,再也不会因为顺序问题拿不到编号:

<xsl:template match="footnote-block">
  <div class="footnote-block">
    <!-- 这里直接用预处理后的脚注编号 + 前置元素@n值 -->
    <xsl:value-of select="preceding-sibling::footnote[1]/@temp-num || '=' || preceding-sibling::*[1]/@n"/>
  </div>
</xsl:template>

额外优化:用xsl:key关联脚注(更灵活的场景)

如果你的脚注和脚注块不是严格的相邻兄弟关系,还可以用<xsl:key>来关联脚注和它们的编号,这样不管位置在哪里都能准确获取:

<!-- 定义key,用节点唯一ID关联脚注 -->
<xsl:key name="footnote-key" match="footnote | appnote" use="generate-id()"/>

<xsl:template match="/">
  <xsl:variable name="numbered-footnotes">
    <xsl:apply-templates select="//footnote | //appnote" mode="assign-footnote-numbers"/>
  </xsl:variable>
  
  <xsl:apply-templates/>
</xsl:template>

<xsl:template match="footnote-block">
  <div class="footnote-block">
    <!-- 通过key从预处理后的集合中找到对应脚注的编号 -->
    <xsl:variable name="target-footnote" select="preceding-sibling::footnote[1]"/>
    <xsl:variable name="footnote-number" select="key('footnote-key', generate-id($target-footnote), $numbered-footnotes)/@temp-num"/>
    <xsl:value-of select="$footnote-number || '=' || preceding-sibling::*[1]/@n"/>
  </div>
</xsl:template>

这个方案的核心就是通过mode把编号计算提前,让脚注块生成时能直接拿到已经确定的编号值,完美解决顺序问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:35:54