如何用XSLT 2.0中的group-by对数据进行分组?

group-by是XSLT 2.0中xsl:for-each-group的必需属性,用于按表达式值自动分组;需配合current-group()和current-grouping-key()使用,并注意空值、大小写等边界处理。

在XSLT 2.0中,group-byxsl:for-each-group 指令的核心属性,用于按指定表达式对节点序列分组。它不依赖预排序,也不要求数据结构特殊,只要表达式能为每个项目生成一个可比较的值(如字符串、数字、日期等),就能自动聚类。

基本语法和结构

分组必须包裹在 xsl:for-each-group 中,不能单独使用 group-by

  • select 属性指定要遍历的节点序列(比如 //item
  • group-by 属性指定分组依据的表达式(比如 @categoryupper-case(name)
  • 每组内可通过 current-group() 获取全部成员,用 current-grouping-key() 获取当前组的键值

常见分组方式示例

按属性值分组
 组键是每个 producttype 属性值,相同 @type 的产品归为一组。

按子元素文本分组
 以 customer/name 的字符串值为键,把同一客户的所有订单归为一组。

按计算结果分组(如首字母、年份)
 按书名首字母分组;也可用 year-from-date(@pubdate) 按出版年份分组。

处理每组数据的常用技巧

进入每组后,通常需要输出汇总或列表:

  • 输出组名(如 “Fiction”)
  • 遍历该组所有节点
  • 配合聚合函数:如 count(current-group()) 得到组大小,sum(current-group()/price) 算总价
  • 若需去重分组(如多个节点有相同键但只算一次),可用 group-by="generate-id()" + 预处理,但更推荐先用 xsl:key 或 distinct-values 配合 for-each

注意边界情况

空值和空白会影响分组结果:

  • group-by="@status" 中,缺失 @status 的节点会被分到同一个隐式空组(键为 xs:string(()),即空序列)
  • 建议显式处理:用 group-by="normalize-space(@status)" 去首尾空格,或 group-by="if(@status) then @status else 'unknown' 统一缺省值
  • 区分大小写:默认敏感,如需忽略,用 upper-case(@role)lower-case()

基本上就这些。掌握 current-group()current-grouping-key() 这两个核心函数,再结合实际路径或计算表达式,就能灵活应对大多数分组需求。不复杂但容易忽略空值和大小写细节。