XSLT 1.0 中唯一可移植的分组方法是 Muenchian 方法,其核心是通过 <xsl:key> 定义键并结合 generate-id() 识别每组首个节点,再用 key() 获取同组全部节点。

XSLT 2.0 的 <xsl:for-each-group> 配合 group-by 是最直观的分组方式,而 XSLT 1.0 没有原生分组指令,必须用 Muenchian 方法模拟——它不是“替代方案”,而是 XSLT 1.0 中唯一被广泛验证、可移植的等效实现。
核心原理:用 key + generate-id 实现“首次出现即代表新组”
Muenchian 分组本质是两步走:先定义键(<xsl:key>),再筛选每个键值下第一个匹配节点。只有这个“首个节点”才触发分组逻辑,其余同组节点通过 key() 函数批量获取。
-
定义键:用
<xsl:key name="k" match="book" use="author"/>建立“作者 → 所有 book 元素”的映射 -
识别组头:用
book[generate-id() = generate-id(key('k', author)[1])]找出每个作者第一次出现的 book -
遍历组内成员:在该节点上下文中调用
key('k', author)拿到全部同作者书籍
典型结构模板(可直接复用)
以下是最简可用骨架,适配任意 XML 结构:
<xsl:key name="by-author" match="book" use="author"/>
<xsl:template match="/">
<div>
<!-- 步骤1:遍历每个唯一 author(即每组的代表节点)-->
<xsl:for-each select="library/book[generate-id() = generate-id(key('by-author', author)[1])]>
<h3><xsl:value-of select="author"/></h3>
<ul>
<!-- 步骤2:取出该 author 下所有 book -->
<xsl:for-each select="key('by-author', author)">
<li><xsl:value-of select="title"/></li>
</xsl:for-each>
</ul>
</xsl:for-each>
</div>
</xsl:template>常见变体与关键细节
实际使用时需注意几个易错点:
-
use 属性支持 XPath 表达式:比如
use="@category"(按属性)、use="concat(substring(title,1,1), '-group')"(首字母分组) -
空值/缺失字段要处理:若 author 可能为空,建议改用
use="normalize-space(author) | 'unknown'"避免生成空组 - 多级分组需嵌套 key:如先按 category 再按 author,可定义两个 key,外层循环选 category 唯一节点,内层再按 author 过滤
-
性能敏感场景慎用 position():Muenchian 本身已高效,但若在
key()内部再加position() mod 2等判断,会显著拖慢
对比 XSLT 2.0 group-by 的差异
虽然功能等价,但行为不完全一致:
-
排序不可控:Muenchian 返回组的顺序取决于源文档中“首个节点”的出现顺序;2.0 的
<xsl:sort>可显式控制组序 -
无法直接获取组大小:XSLT 1.0 中需用
count(key('k', author))手动统计;2.0 有current-group()和current-grouping-key() - 不支持 group-adjacent 或 group-ending-with:这些高级模式仅 2.0 支持,1.0 必须靠预处理或额外逻辑模拟










