XSLT 1.0按值分组求和:基于给定XML实现分组汇总
嘿,搞定XSLT 1.0的分组求和其实用经典的Muenchian分组法就可以了,毕竟XSLT 1.0没有原生的group-by指令,这个方法是业界通用的解决方案。我结合你给的XML结构,给你写个完整的实现方案:
核心思路
Muenchian分组的关键是先定义一个key,用来把相同article/code的节点归为一组,然后遍历每个唯一的分组键,对组内的节点进行求和操作。
完整XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- 定义分组键:按article的code进行分组 --> <xsl:key name="articleByCode" match="article" use="code"/> <xsl:template match="/articles"> <articles> <!-- 遍历每个group节点 --> <xsl:for-each select="group"> <group> <!-- 保留原group的code和description --> <code><xsl:value-of select="code"/></code> <description><xsl:value-of select="description"/></description> <!-- 筛选当前group下的唯一article分组(每个code只取第一个节点) --> <xsl:for-each select="article[generate-id() = generate-id(key('articleByCode', code)[1])]"> <summary_article> <!-- 保留article的基本信息 --> <code><xsl:value-of select="code"/></code> <description><xsl:value-of select="description"/></description> <!-- 对当前分组的qty求和 --> <total_qty><xsl:value-of select="sum(key('articleByCode', code)/qty)"/></total_qty> <!-- 对当前分组的amount求和 --> <total_amount><xsl:value-of select="sum(key('articleByCode', code)/amount)"/></total_amount> <!-- 保留menu_code(如果同分组的menu_code一致,取第一个即可) --> <menu_code><xsl:value-of select="menu_code"/></menu_code> </summary_article> </xsl:for-each> </group> </xsl:for-each> </articles> </xsl:template> </xsl:stylesheet>
关键部分解释
<xsl:key>定义:match="article"表示我们要分组的节点是article,use="code"表示分组的依据是article下的code节点值。- 唯一分组筛选:
generate-id() = generate-id(key('articleByCode', code)[1])这个条件用来选出每个分组的第一个节点,确保每个code只处理一次。 - 求和操作:
sum(key('articleByCode', code)/qty)会找到所有和当前节点code相同的article,然后对它们的qty节点值求和,amount同理。
输出效果示例
针对你提供的XML片段(假设第二个article的amount是10.00),输出会是这样:
<articles> <group> <code>1000</code> <description>GROUP A</description> <summary_article> <code>2600</code> <description>Article A</description> <total_qty>24.00</total_qty> <total_amount>24.00</total_amount> <menu_code>true</menu_code> </summary_article> </group> </articles>
如果你的需求是跨group分组(而不是每个group内单独分组),只需要调整xsl:for-each的选择范围,把group/article改成//article即可,逻辑是一样的。
内容的提问来源于stack exchange,提问作者Kevin Vizcarra Paredes




