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

如何用XSLT 2.0/3.0将相邻XML元素分组为嵌套有序列表

问题描述

手里有一份层级结构混乱的XML,要把它转成多级嵌套的有序列表,要求如下:

  • 只处理name属性是level_1到level_6的<p>元素,按层级嵌套
  • name为level_0的元素,以及<note>这类非目标元素直接忽略
  • 转换逻辑要通用,能适配level_1到level_6的任意层级嵌套

源XML:

<root>
    <p name="level_0">Value excluded from grouping.</p>
    <p name="level_1">Value 1</p>
    <p name="level_1">Value 2</p>
    <p name="level_2">Value 3</p>
    <p name="level_2">Value 4</p>
    <p name="level_1">Value 5</p>
    <note>Some text</note>
    <p name="level_1">Another Value 1</p>
    <p name="level_2">Another Value 2</p>
    <p name="level_2">Another Value 3</p>
    <p name="level_3">Another Value 4</p>
    <p name="level_3">Another Value 5</p>
    <p name="level_4">Another Value 3</p>
    <note>Some text</note>
    <p name="level_4">Another Value 4</p>
    <p name="level_1">Another Value 5</p>
    <p name="level_0">Value excluded from grouping.</p>
</root>

期望输出:

<ol class="ol-level_1">
    <li class="li-level_1">Value 1</li>
    <li class="li-level_1">
        Value 2
        <ol class="ol-level_2">
            <li class="li-level_2">Value 3</li>
            <li class="li-level_2">Value 4</li>
        </ol>
    </li>
    <li class="li-level_1">Value 5</li>
    <li class="li-level_1">
        Another Value 1
        <ol class="ol-level_2">
            <li class="li-level_2">Another Value 2</li>
            <li class="li-level_2">
                Another Value 3
                <ol class="ol-level_3">
                    <li class="li-level_3">Another Value 4</li>
                    <li class="li-level_3">
                        Another Value 5
                        <ol class="ol-level_4">
                            <li class="li-level_4">Another Value 3</li>
                            <li class="li-level_4">Another Value 4</li>
                        </ol>
                    </li>
                </ol>
            </li>
        </ol>
    </li>
    <li class="li-level_1">Another Value 5</li>
</ol>

你尝试的XSLT代码(不符合预期):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:math="http://www.w3.org/2005/xpath-functions/math" exclude-result-prefixes="xs math"
  version="2.0">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="root">
    <ol class="ol-{@name}">
      <xsl:for-each-group select="*" group-adjacent="@name">
        <li class="li-{@name}">
          <xsl:value-of select="current-grouping-key()"/>
          <ol class="ol-{@name}">
            <xsl:for-each select="current-group()">
              <li class="li-{@name}">
                <xsl:value-of select="."/>
              </li>
            </xsl:for-each>
          </ol>
        </li>
      </xsl:for-each-group>
    </ol>
  </xsl:template>
</xsl:stylesheet>
解决方案

问题分析

你之前的代码主要问题:

  1. 错误使用group-adjacent="@name",同层级元素可能不相邻,且未处理层级嵌套逻辑
  2. 错误引用root的@name属性(root根本没有这个属性)
  3. 未过滤level_0和非目标元素
  4. 仅做简单分组,未实现“子层级嵌套到父层级li内”的核心需求

正确的XSLT代码(通用适配level_1到level_6)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output method="xml" indent="yes"/>
    
    <!-- 忽略不需要处理的元素 -->
    <xsl:template match="p[@name='level_0'] | *[not(self::p) or not(starts-with(@name, 'level_'))]"/>
    
    <!-- 处理root,触发level_1元素的分组逻辑 -->
    <xsl:template match="root">
        <xsl:apply-templates select="p[@name='level_1']" mode="group"/>
    </xsl:template>
    
    <!-- 递归处理层级分组的核心模板 -->
    <xsl:template match="p" mode="group">
        <xsl:param name="current-level" select="number(substring-after(@name, 'level_'))"/>
        <li class="li-{@name}">
            <xsl:value-of select="."/>
            <!-- 查找当前元素所属的子层级元素 -->
            <xsl:variable name="children" select="following-sibling::p[
                number(substring-after(@name, 'level_')) = $current-level + 1
                and not(following-sibling::p[number(substring-after(@name, 'level_')) &lt;= $current-level][1] &lt;&lt; .)
            ]"/>
            <xsl:if test="$children">
                <ol class="ol-{substring-after(@name, 'level_') + 1}">
                    <xsl:apply-templates select="$children" mode="group"/>
                </ol>
            </xsl:if>
        </li>
        <!-- 处理当前元素之后的同层级下一个元素 -->
        <xsl:apply-templates select="following-sibling::p[
            number(substring-after(@name, 'level_')) = $current-level
            and not(preceding-sibling::p[number(substring-after(@name, 'level_')) &lt; $current-level][1] &lt;&lt; .)
        ][1]" mode="group"/>
    </xsl:template>
    
    <!-- 给最外层level_1元素包裹ol -->
    <xsl:template match="p[@name='level_1'][1]" mode="group">
        <ol class="ol-level_1">
            <xsl:next-match/>
        </ol>
    </xsl:template>
</xsl:stylesheet>

核心逻辑说明

  1. 过滤规则:通过模板匹配直接忽略level_0和非目标元素,只保留level_1到level_6的<p>
  2. 递归层级处理:用mode="group"的模板递归遍历每个层级,通过substring-after提取层级数字,动态判断子层级关系
  3. 子元素范围判定:通过following-sibling和位置逻辑,精准定位当前元素所属的子层级元素(即当前元素之后、下一个同层级或更高层级元素之前的所有子层级元素)
  4. 通用适配:无需硬编码每个level的规则,通过动态计算层级数字,自动适配level_1到level_6的嵌套结构

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 19:55:53