XSLT实现文档中任意位置<note>元素的连续编号问题
解决XSLT中嵌套元素的全局连续编号问题
核心思路
要实现文档顺序的连续编号,关键是准确统计当前<note>节点之前所有已出现的<note>节点总数,再加1得到当前编号。无需分段统计,直接通过XPath轴或<xsl:number>实现全局计数即可。
示例输入XML
<doc> <p>第一段文本<note>注释1</note>,包含<q>引用内容<note>注释2</note></q>,继续文本<note>注释3</note></p> <p>第二段文本<note>注释4</note></p> </doc>
具体实现方案
方案1:XSLT 2.0+ 简洁写法
利用XPath 2.0的文档顺序比较运算符<<,直接统计所有在当前节点之前的<note>:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 处理正文,生成注释引用 --> <xsl:template match="note"> <sup> <xsl:variable name="note-num" select="count(//note[. << current()]) + 1"/> <a href="#note-{$note-num}" id="ref-{$note-num}"> [<xsl:value-of select="$note-num"/>] </a> </sup> </xsl:template> <!-- 保留原文本结构(如<p>、<q>) --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- 生成页面末尾的注释块 --> <xsl:template match="/"> <html> <body> <xsl:apply-templates/> <div class="notes"> <h3>注释</h3> <ol> <xsl:apply-templates select="//note" mode="note-block"/> </ol> </div> </body> </html> </xsl:template> <xsl:template match="note" mode="note-block"> <xsl:variable name="note-num" select="count(//note[. << current()]) + 1"/> <li id="note-{$note-num}"> <xsl:value-of select="."/> <a href="#ref-{$note-num}">↑</a> </li> </xsl:template> </xsl:stylesheet>
方案2:兼容XSLT 1.0的写法
XSLT 1.0不支持<<,可以用generate-id()比较节点唯一性(先出现的节点ID更小):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="note"> <sup> <xsl:variable name="note-num" select="count(/descendant::note[generate-id(.) < generate-id(current())]) + 1"/> <a href="#note-{$note-num}" id="ref-{$note-num}"> [<xsl:value-of select="$note-num"/>] </a> </sup> </xsl:template> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="/"> <html> <body> <xsl:apply-templates/> <div class="notes"> <h3>注释</h3> <ol> <xsl:apply-templates select="//note" mode="note-block"/> </ol> </div> </body> </html> </xsl:template> <xsl:template match="note" mode="note-block"> <xsl:variable name="note-num" select="count(/descendant::note[generate-id(.) < generate-id(current())]) + 1"/> <li id="note-{$note-num}"> <xsl:value-of select="."/> <a href="#ref-{$note-num}">↑</a> </li> </xsl:template> </xsl:stylesheet>
方案3:XSLT 2.0+ 高性能写法
用<xsl:number>的level="any"属性,直接按文档顺序统计所有层级的<note>,性能优于全局count():
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="note"> <sup> <xsl:variable name="note-num"> <xsl:number level="any" count="note"/> </xsl:variable> <a href="#note-{$note-num}" id="ref-{$note-num}"> [<xsl:value-of select="$note-num"/>] </a> </sup> </xsl:template> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="/"> <html> <body> <xsl:apply-templates/> <div class="notes"> <h3>注释</h3> <ol> <xsl:apply-templates select="//note" mode="note-block"/> </ol> </div> </body> </html> </xsl:template> <xsl:template match="note" mode="note-block"> <xsl:variable name="note-num"> <xsl:number level="any" count="note"/> </xsl:variable> <li id="note-{$note-num}"> <xsl:value-of select="."/> <a href="#ref-{$note-num}">↑</a> </li> </xsl:template> </xsl:stylesheet>
问题根源说明
之前的代码编号重置,是因为只统计了当前<p>之前的<note>,没有包含当前<p>内、当前<note>之前的嵌套<note>。上述方案通过全局统计文档顺序在前的所有<note>,不管节点层级,自然解决了重置问题。
内容的提问来源于stack exchange,提问作者Mikesname
相关产品推荐
相关产品推荐

