使用XSLT展平嵌套XML元素的属性问题求助
用XSLT合并属性值展平嵌套XML元素树
我来帮你搞定这个嵌套XML展平的问题!核心思路是递归遍历嵌套的node元素,把路径上的属性值拼接成完整路径,同时提取目标节点的value值。下面直接上可运行的方案,包括基础场景和复杂情况的处理。
先明确场景示例
输入XML(嵌套结构)
<root> <node id="A"> <node id="B" value="123"/> <node id="C"> <node id="D" value="456"/> <node id="E"> <node id="F" value="789"/> </node> </node> </node> </root>
期望输出(展平结构)
<flattened> <item path="A/B" value="123"/> <item path="A/C/D" value="456"/> <item path="A/C/E/F" value="789"/> </flattened>
实现XSLT代码(兼容XSLT 1.0/2.0)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <!-- 初始化:创建输出根节点,选中所有带value属性的node --> <xsl:template match="/"> <flattened> <xsl:apply-templates select="//node[@value]"/> </flattened> </xsl:template> <!-- 处理每个带value的node,生成展平后的item元素 --> <xsl:template match="node[@value]"> <item> <!-- 调用递归模板拼接完整路径 --> <xsl:attribute name="path"> <xsl:call-template name="build-full-path"> <xsl:with-param name="current-node" select="."/> </xsl:call-template> </xsl:attribute> <!-- 直接提取当前node的value属性 --> <xsl:attribute name="value" select="@value"/> </item> </xsl:template> <!-- 递归模板:从当前node向上遍历父节点,拼接id路径 --> <xsl:template name="build-full-path"> <xsl:param name="current-node"/> <xsl:param name="temp-path" select="''"/> <!-- 如果当前节点有父级node,继续向上递归 --> <xsl:if test="$current-node/parent::node"> <xsl:call-template name="build-full-path"> <xsl:with-param name="current-node" select="$current-node/parent::node"/> <xsl:with-param name="temp-path" select="concat('/', $current-node/@id, $temp-path)"/> </xsl:call-template> </xsl:if> <!-- 拼接最终路径:顶层node直接输出id,后续节点去掉开头的斜杠 --> <xsl:choose> <xsl:when test="not($current-node/parent::node)"> <xsl:value-of select="$current-node/@id"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="substring($temp-path, 2)"/> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- 忽略XML中的空白文本节点,避免输出冗余内容 --> <xsl:template match="text()"/> </xsl:stylesheet>
代码工作原理拆解
- 根模板:匹配整个XML文档,创建输出的
flattened根元素,然后选中所有带有value属性的node元素进行处理。 - 带value的node处理模板:为每个符合条件的
node生成item元素,调用递归模板生成path属性,同时直接复制value属性。 - 递归路径拼接模板:从当前
node开始,向上遍历所有父级node,把每个节点的id拼接到路径前面,最终生成从顶层到当前节点的完整路径。 - 空白节点处理:忽略XML中的空白文本,避免输出多余的空行或空格。
复杂场景适配
如果你的XML存在特殊情况,可以做以下调整:
- 只处理最深层带value的node:把
select="//node[@value]"改成select="//node[@value and not(node)]",这样只会选择没有子node的带value节点。 - node没有id属性:在拼接路径时加判断,比如用
concat('/', if($current-node/@id) then $current-node/@id else 'unknown', $temp-path),给无id的节点加默认标识。 - 自定义路径分隔符:把递归模板里的
concat('/', $current-node/@id, $temp-path)改成你需要的分隔符,比如.或-,比如concat('.', $current-node/@id, $temp-path)。 - 中间节点也带value:当前模板会自动处理所有带
value的节点,不管它有没有子节点。比如某个node自己有value同时还有子node,输出会同时包含这个节点和它的子节点的展平记录。
内容的提问来源于stack exchange,提问作者Hiko Haieto
相关产品推荐
相关产品推荐

