如何使用XSLT 2.0将平级引用XML转换为分层XML
解决平级XML节点通过ID引用生成分层结构的XSLT 2.0方案
我来帮你搞定这个问题!你的场景是典型的基于ID引用构建层级结构的需求,之前递归模板没成功,大概率是没利用好XSLT的key来高效查找节点,加上没处理好referenceID里多ID的拆分逻辑。
首先提个小细节:你给出的原始XML缺少根节点,这是无效的XML,我们需要先给它套一个根节点,比如<Root>,调整后的原始XML如下:
<?xml version="1.0" encoding="UTF-8"?> <Root> <Data id="1" referenceID="2 3"> Text1 </Data> <Data id="2" referenceID="4"> Text2 </Data> <Data id="3" referenceID="5"> Text3 </Data> <Data id="4"> Text4 </Data> <Data id="5"> Text5 </Data> </Root>
接下来是实现需求的XSLT 2.0代码,我会逐段解释:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 定义key,通过id快速查找对应的Data节点 --> <xsl:key name="data-by-id" match="Data" use="@id"/> <!-- 主模板:匹配根节点,启动层级构建 --> <xsl:template match="/Root"> <xsl:call-template name="build-level"> <!-- 传入顶层节点:这里是id为1的Data,你可以根据实际情况调整 --> <xsl:with-param name="node" select="key('data-by-id', '1')"/> <!-- 初始层级为1 --> <xsl:with-param name="level" select="1"/> </xsl:call-template> </xsl:template> <!-- 递归模板:构建层级结构 --> <xsl:template name="build-level"> <xsl:param name="node"/> <xsl:param name="level"/> <!-- 生成对应层级的元素,比如Level1、Level2 --> <xsl:element name="Level{$level}"> <!-- 输出当前节点的文本内容(去掉多余空格) --> <xsl:value-of select="normalize-space($node/text())"/> <!-- 处理当前节点的referenceID:如果存在则拆分ID并递归 --> <xsl:if test="$node/@referenceID"> <!-- 用tokenize拆分空格分隔的ID序列 --> <xsl:variable name="ref-ids" select="tokenize($node/@referenceID, '\s+')"/> <!-- 遍历每个引用ID,找到对应节点并递归调用模板 --> <xsl:for-each select="$ref-ids"> <xsl:call-template name="build-level"> <xsl:with-param name="node" select="key('data-by-id', .)"/> <xsl:with-param name="level" select="$level + 1"/> </xsl:call-template> </xsl:for-each> </xsl:if> </xsl:element> </xsl:template> </xsl:stylesheet>
关键逻辑解释:
<xsl:key>定义:相当于给所有Data节点建立了一个“索引”,通过id可以瞬间找到对应的节点,比遍历所有节点效率高很多,尤其是节点数量多的时候。- 递归模板
build-level:- 接收两个参数:当前要处理的
Data节点,以及当前的层级数。 - 用
<xsl:element name="Level{$level}">动态生成对应层级的元素(比如层级1生成<Level1>,层级2生成<Level2>)。 - 用
tokenize()函数把referenceID里的空格分隔ID拆分成序列,这是XSLT 2.0新增的实用特性,完美解决多ID的处理问题。 - 遍历每个拆分后的ID,找到对应的
Data节点,然后递归调用自身,层级数加1,以此构建嵌套结构。
- 接收两个参数:当前要处理的
转换后的输出结果:
<?xml version="1.0" encoding="UTF-8"?> <Level1>Text1 <Level2>Text2 <Level3>Text4</Level3> </Level2> <Level2>Text3 <Level3>Text5</Level3> </Level2> </Level1>
如果你的顶层节点不是固定的id=1,而是需要自动找到所有未被其他节点引用的节点,只需要修改主模板里的select逻辑即可:
<xsl:with-param name="node" select="Data[not(key('data-by-id', //Data/@referenceID))]"/>
这样就能自动定位所有没有被引用的顶层节点,适配更复杂的场景。
内容的提问来源于stack exchange,提问作者M4rk0444
相关产品推荐
相关产品推荐

