XSLT中为特定XML元素生成独立序列编号的问题求助
问题:XSLT中为同类型XML元素按父级Item分组生成序列编号
需求说明
需要实现的效果是:每个<Item>节点下的<ZDoc>和<TDoc>各自独立从1开始递增编号,同类型元素在所属Item内连续编号,期望输出如下:
<Item> <ZDoc> <seq>1</seq> </ZDoc> <ZDoc> <seq>2</seq> </ZDoc> <TDoc> <seq>1</seq> </TDoc> <TDoc> <seq>2</seq> </TDoc> </Item> <Item> <ZDoc> <seq>1</seq> </ZDoc> <ZDoc> <seq>2</seq> </ZDoc> <TDoc> <seq>1</seq> </TDoc> <TDoc> <seq>2</seq> </TDoc> </Item>
尝试过的方法及问题
方法一:全局计数模板
编写的模板代码:
<xsl:template name="my_func()"> <xsl:param name="id_name" select="''"/> <!-- Count elements --> <xsl:variable name="seq" select="count(//*[local-name() = $global-id]) + 1"/> <!-- Output the generated sequence number --> <xsl:value-of select="$seq"/> </xsl:template>
问题:
- 参数名与变量引用不匹配:模板参数是
id_name,但变量中用了未定义的$global-id,导致计数逻辑失效; - 使用全局路径
//查找元素,会统计整个文档内的同类型元素,而非当前<Item>下的,因此无法实现按Item分组编号,最终输出大量编号1。
方法二:使用position()的模板
编写的模板代码:
<xsl:template name="my_func()"> <xsl:param name="id" select="()"/> <xsl:variable name="auto_number" as="xs:integer" select="position() "/> <xsl:sequence select="xs:integer($auto_number)"/> </xsl:template>
问题:position()返回的是当前节点在所有兄弟节点中的位置,而非同类型兄弟的位置。比如<Item>下的节点顺序是ZDoc、ZDoc、TDoc、TDoc,TDoc的position()值为3、4,调用模板时会直接复用这个值,导致TDoc的编号从2/3开始(取决于调用上下文),无法从1重新计数。
解决方案
核心逻辑:针对每个元素,计算它在所属父节点下的同类型兄弟节点中的位置,而非全局或所有兄弟的位置。
方案1:直接使用XPath计算(无需模板)
在匹配ZDoc/TDoc的模板中,直接用XPath计算位置:
<!-- 匹配Item节点,保留结构并处理子节点 --> <xsl:template match="Item"> <xsl:copy> <xsl:apply-templates select="ZDoc | TDoc"/> </xsl:copy> </xsl:template> <!-- 匹配ZDoc和TDoc,生成seq编号 --> <xsl:template match="ZDoc | TDoc"> <xsl:copy> <seq> <!-- 统计当前节点之前的同类型兄弟节点数量,加1得到编号 --> <xsl:value-of select="count(preceding-sibling::*[local-name() = local-name(current())]) + 1"/> </seq> </xsl:copy> </xsl:template>
方案2:复用模板(如果需要通用逻辑)
如果需要将编号逻辑封装为模板复用,可传递当前节点作为参数:
<!-- 通用编号模板 --> <xsl:template name="get-sequence"> <xsl:param name="current-node"/> <xsl:value-of select="count($current-node/preceding-sibling::*[local-name() = local-name($current-node)]) + 1"/> </xsl:template> <!-- 调用模板生成编号 --> <xsl:template match="ZDoc | TDoc"> <xsl:copy> <seq> <xsl:call-template name="get-sequence"> <xsl:with-param name="current-node" select="."/> </xsl:call-template> </seq> </xsl:copy> </xsl:template>
内容的提问来源于stack exchange,提问作者RamAlx
相关产品推荐
相关产品推荐

