You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请求递归XSLT实现扁平XML结构转多层级结构

问题:将扁平XML转换为嵌套层级结构,理解XSLT模板与for-each的区别

我有如下扁平结构的XML:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Report>
<BCOT>
<Detail_Collection>
<Detail>
<ElementId>Element1</ElementId>
<ParentElementId>Element0</ParentElementId>
</Detail>
<Detail>
<ElementId>Element2</ElementId>
<ParentElementId>Element1</ParentElementId>
</Detail>
<Detail>
<ElementId>Element3</ElementId>
<ParentElementId>Element2</ParentElementId>
</Detail>
<Detail>
<ElementId>Element4</ElementId>
<ParentElementId>Element3</ParentElementId>
</Detail>
<Detail>
<ElementId>Element5</ElementId>
<ParentElementId>Element3</ParentElementId>
</Detail>
<Detail>
<ElementId>Element6</ElementId>
<ParentElementId>Element1</ParentElementId>
</Detail>
<Detail>
<ElementId>Element7</ElementId>
<ParentElementId>Element6</ParentElementId>
</Detail>
<Detail>
<ElementId>Element8</ElementId>
<ParentElementId>Element7</ParentElementId>
</Detail>
<Detail>
<ElementId>Element9</ElementId>
<ParentElementId>Element8</ParentElementId>
</Detail>
<Detail>
<ElementId>Element10</ElementId>
<ParentElementId>Element9</ParentElementId>
</Detail>
<Detail>
<ElementId>Element11</ElementId>
<ParentElementId>Element10</ParentElementId>
</Detail>
<Detail>
<ElementId>Element12</ElementId>
<ParentElementId>Element11</ParentElementId>
</Detail>
<Detail>
<ElementId>Element13</ElementId>
<ParentElementId>Element11</ParentElementId>
</Detail>
<Detail>
<ElementId>Element14</ElementId>
<ParentElementId>Element11</ParentElementId>
</Detail>
<Detail>
<ElementId>Element15</ElementId>
<ParentElementId>Element9</ParentElementId>
</Detail>
<Detail>
<ElementId>Element16</ElementId>
<ParentElementId>Element15</ParentElementId>
</Detail>
<Detail>
<ElementId>Element17</ElementId>
<ParentElementId>Element16</ParentElementId>
</Detail>
<Detail>
<ElementId>Element18</ElementId>
<ParentElementId>Element16</ParentElementId>
</Detail>
</Detail_Collection>
</BCOT>
</Report>

需要将其转换为如下多层级嵌套结构的XML:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Report>
<BCOT>
<Detail_Collection>
<Detail>
<ElementId>Element1</ElementId>
<ParentElementId>Element0</ParentElementId>
<Detail>
<ElementId>Element2</ElementId>
<ParentElementId>Element1</ParentElementId>
<Detail>
<ElementId>Element3</ElementId>
<ParentElementId>Element2</ParentElementId>
<Detail>
<ElementId>Element4</ElementId>
<ParentElementId>Element3</ParentElementId>
</Detail>
<Detail>
<ElementId>Element5</ElementId>
<ParentElementId>Element3</ParentElementId>
</Detail>
</Detail>
</Detail>
<Detail>
<ElementId>Element6</ElementId>
<ParentElementId>Element1</ParentElementId>
<Detail>
<ElementId>Element7</ElementId>
<ParentElementId>Element6</ParentElementId>
<Detail>
<ElementId>Element8</ElementId>
<ParentElementId>Element7</ParentElementId>
<Detail>
<ElementId>Element9</ElementId>
<ParentElementId>Element8</ParentElementId>
<Detail>
<ElementId>Element10</ElementId>
<ParentElementId>Element9</ParentElementId>
<Detail>
<ElementId>Element11</ElementId>
<ParentElementId>Element10</ParentElementId>
<Detail>
<ElementId>Element12</ElementId>
<ParentElementId>Element11</ParentElementId>
</Detail>
<Detail>
<ElementId>Element13</ElementId>
<ParentElementId>Element11</ParentElementId>
</Detail>
<Detail>
<ElementId>Element14</ElementId>
<ParentElementId>Element11</ParentElementId>
</Detail>
</Detail>
</Detail>
<Detail>
<ElementId>Element15</ElementId>
<ParentElementId>Element9</ParentElementId>
<Detail>
<ElementId>Element16</ElementId>
<ParentElementId>Element15</ParentElementId>
<Detail>
<ElementId>Element17</ElementId>
<ParentElementId>Element16</ParentElementId>
</Detail>
<Detail>
<ElementId>Element18</ElementId>
<ParentElementId>Element16</ParentElementId>
</Detail>
</Detail>
</Detail>
</Detail>
</Detail>
</Detail>
</Detail>
</Detail>
</Detail_Collection>
</BCOT>
</Report>

我了解到递归任务更适合使用XSLT模板,但始终没能完全理解模板与for-each的区别。曾尝试固定次数循环的实现方式,但希望通过递归完成转换,能否提供帮助?


解答

先搞懂模板(xsl:template)和for-each的核心区别

  • for-each是迭代式处理:它会把你指定的节点集拉到同一个上下文里逐个处理,适合简单的循环场景,但处理嵌套树形结构时,你得手动维护层级关系,代码会变得非常冗余且难以扩展。
  • 模板是声明式+递归式处理:模板基于节点匹配规则触发,每个模板只负责处理特定类型的节点。当需要处理子节点时,只需要调用匹配子节点的模板,XSLT会自动切换上下文,完美适配递归场景,代码更简洁、易维护。

实现递归转换的XSLT代码

下面是针对你的需求写的XSLT,核心就是用模板递归匹配子节点:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- 复制所有未被匹配的节点(保留根结构、容器等) -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <!-- 清空默认的Detail_Collection处理,我们要重新生成嵌套结构 -->
    <xsl:template match="Detail_Collection">
        <xsl:copy>
            <!-- 找到所有父节点是Element0的Detail,作为根节点开始递归 -->
            <xsl:apply-templates select="//Detail[ParentElementId='Element0']"/>
        </xsl:copy>
    </xsl:template>

    <!-- 核心递归模板:处理单个Detail节点 -->
    <xsl:template match="Detail">
        <xsl:copy>
            <!-- 复制当前节点的子元素(ElementId和ParentElementId) -->
            <xsl:apply-templates select="ElementId|ParentElementId"/>
            <!-- 递归查找所有以当前ElementId为父节点的Detail子节点,调用同样的模板 -->
            <xsl:apply-templates select="//Detail[ParentElementId=current()/ElementId]"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

代码逻辑解释

  1. 默认复制模板:第一个模板会复制所有未被专门匹配的节点,保证Report、BCOT这些上层结构原样输出。
  2. 重写Detail_Collection处理:清空原来的扁平Detail列表,只筛选出父节点是Element0的Detail作为嵌套结构的根节点,触发递归处理。
  3. 递归Detail模板:每个Detail节点先复制自己的ElementId和ParentElementId,然后自动查找所有以自己为父节点的子Detail,对这些子节点再次应用本模板,形成嵌套层级。

这样运行这个XSLT就能把你的扁平XML转换成目标的嵌套结构啦。


内容的提问来源于stack exchange,提问作者Martin Purdy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 13:12:40