XSLT实现XML中x元素空n属性的递增赋值问题
正确填充XML中空
n属性的XSLT实现 问题背景
输入XML:
<div> <p> <x n="1"/> <x n=""/> <x n=""/> <x n=""/> <x n="190"/> <x n=""/> <x n=""/> </p> </div>
需求:将x元素的空n属性从最近的已有有效值开始递增填充,目标结果:
<div> <p> <x n="1"/> <x n="2"/> <x n="3"/> <x n="4"/> <x n="190"/> <x n="191"/> <x n="192"/> </p> </div>
原尝试的XSLT方案(仅部分生效):
<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="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="x[@n = '']/@n"> <xsl:attribute name="n" select="preceding::x[number(@n)]/@n + 1"/> </xsl:template> </xsl:stylesheet>
原方案问题:XSLT基于原始节点树处理,而非处理后的结果。第二个空n的x能找到原始树中第一个有效值,但后续空节点的前序在原始树里仍无有效值,无法继续递增。
正确的XSLT实现
通过递归遍历并实时跟踪当前数值,实现连续递增填充:
<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="xml" version="1.0" encoding="UTF-8" indent="yes"/> <!-- 身份模板:复制未特殊处理的节点和属性 --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <!-- 处理包含x元素的p节点,初始化数值并启动递归处理 --> <xsl:template match="p"> <xsl:copy> <xsl:variable name="x-nodes" select="x"/> <!-- 初始化当前数值:取第一个x的有效值,无有效值则默认设为1 --> <xsl:variable name="initial-val" as="xs:integer"> <xsl:choose> <xsl:when test="$x-nodes[1]/@n != ''"> <xsl:sequence select="xs:integer($x-nodes[1]/@n)"/> </xsl:when> <xsl:otherwise>1</xsl:otherwise> </xsl:choose> </xsl:variable> <xsl:call-template name="process-x"> <xsl:with-param name="nodes" select="$x-nodes"/> <xsl:with-param name="current-val" select="$initial-val"/> </xsl:call-template> </xsl:copy> </xsl:template> <!-- 递归处理x节点的模板 --> <xsl:template name="process-x"> <xsl:param name="nodes" as="element(x)*"/> <xsl:param name="current-val" as="xs:integer"/> <xsl:if test="$nodes"> <xsl:variable name="current-node" select="$nodes[1]"/> <xsl:copy> <xsl:choose> <!-- 节点n非空:复制属性,更新当前数值 --> <xsl:when test="$current-node/@n != ''"> <xsl:attribute name="n" select="$current-node/@n"/> <xsl:call-template name="process-x"> <xsl:with-param name="nodes" select="$nodes[position() > 1]"/> <xsl:with-param name="current-val" select="xs:integer($current-node/@n)"/> </xsl:call-template> </xsl:when> <!-- 节点n为空:使用当前数值,递增加一 --> <xsl:otherwise> <xsl:attribute name="n" select="$current-val"/> <xsl:call-template name="process-x"> <xsl:with-param name="nodes" select="$nodes[position() > 1]"/> <xsl:with-param name="current-val" select="$current-val + 1"/> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:copy> </xsl:if> </xsl:template> </xsl:stylesheet>
方案说明
- 身份模板保证XML原有结构不被破坏,仅处理需要修改的
x元素。 - 针对
p节点,先获取所有x节点序列,初始化当前数值为第一个x的有效值(无有效值则默认设为1,可按需调整)。 - 递归模板
process-x逐个处理x节点:- 若当前节点
n非空,直接复制属性并更新当前数值。 - 若当前节点
n为空,使用当前数值填充,然后将数值加1传递给下一个节点。
- 若当前节点
- 基于处理过程中的实时数值跟踪,而非原始节点树,实现连续递增填充。
内容的提问来源于stack exchange,提问作者Thomas Hansen
相关产品推荐
相关产品推荐

