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

如何用XSLT group-starting-with按Lv属性结构化扁平XML

用XSLT 2/3的for-each-group实现按Lv属性嵌套扁平XML

核心思路

利用for-each-group的group-starting-with属性,以当前层级的起始元素作为分组标记,自动将后续属于该层级的子元素归入同一组,再递归处理组内的子层级元素。关键是通过Lv属性的大小关系判断元素的归属层级:后续元素的Lv必须大于当前组起始元素的Lv,且直到遇到第一个Lv≤当前起始元素Lv的元素为止,这些元素都属于当前组的子元素。

示例输入(扁平XML)

<root>
  <item pos="1" Lv="1" val="Rule A"/>
  <item pos="2" Lv="2" val="Subrule A1"/>
  <item pos="3" Lv="3" val="Sub-subrule A1a"/>
  <item pos="4" Lv="2" val="Subrule A2"/>
  <item pos="5" Lv="1" val="Rule B"/>
  <item pos="6" Lv="2" val="Subrule B1"/>
</root>

XSLT 3实现代码

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>

  <!-- 处理根节点,初始化顶层Lv=1的分组 -->
  <xsl:template match="root">
    <structured-root>
      <xsl:for-each-group select="item" group-starting-with="item[@Lv = 1]">
        <xsl:call-template name="nest-items"/>
      </xsl:for-each-group>
    </structured-root>
  </xsl:template>

  <!-- 递归嵌套模板:处理当前层级元素及其子元素 -->
  <xsl:template name="nest-items">
    <!-- 生成当前层级的节点 -->
    <item level="{current-group()[1]/@Lv}" value="{current-group()[1]/@val}">
      <!-- 筛选当前组中除起始元素外的所有候选子元素 -->
      <xsl:variable name="child-candidates" select="current-group()[position() > 1]"/>
      
      <!-- 对候选子元素按下一层级分组 -->
      <xsl:if test="$child-candidates">
        <xsl:for-each-group select="$child-candidates" 
                            group-starting-with="item[@Lv = current-group()[1]/@Lv + 1]">
          <xsl:call-template name="nest-items"/>
        </xsl:for-each-group>
      </xsl:if>
    </item>
  </xsl:template>
</xsl:stylesheet>

关键逻辑说明

  1. 顶层分组:group-starting-with="item[@Lv = 1]"会将所有item按Lv=1的元素拆分,每个组包含一个顶层规则及其所有下属子规则。
  2. 子层级分组:在递归模板中,用group-starting-with="item[@Lv = current-group()[1]/@Lv + 1]"定位当前层级的直接子元素,自动将后续属于该子层级的元素归入同一组。
  3. 归属判断:current-group()会自动包含从当前起始元素到下一个符合group-starting-with条件的元素之间的所有节点,无需手动判断后续兄弟的归属——XSLT会自动处理边界。

转换后输出(结构化XML)

<structured-root>
   <item level="1" value="Rule A">
      <item level="2" value="Subrule A1">
         <item level="3" value="Sub-subrule A1a"/>
      </item>
      <item level="2" value="Subrule A2"/>
   </item>
   <item level="1" value="Rule B">
      <item level="2" value="Subrule B1"/>
   </item>
</structured-root>

特殊情况适配

如果你的Lv属性不是连续递增(比如存在Lv=1直接跳到Lv=3的情况),可以修改group-starting-with的条件为:

group-starting-with="item[@Lv > current-group()[1]/@Lv and not(preceding-sibling::item[@Lv > current-group()[1]/@Lv and @Lv < current()/@Lv])]"

该条件会将当前起始元素之后、第一个Lv更大且没有中间层级的元素作为子组起始。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 18:57:13