如何在XSLT中分组后获取行数及字段总和并添加至头部
解决方案:给XSLT添加统计头部(总行数+金额求和)
搞定这个需求其实很清晰——咱们需要先把所有要生成的EX/ER行的信息预收集起来,算出总行数、amount1总和、amount2总和,再把统计结果放在输出最前面,最后输出原来的行内容。我分两个版本给你写方案,适配不同的XSLT处理器:
先给个示例XML(方便你对应自己的结构)
假设你的XML结构大概是这样的:
<workers> <worker> <name>Alice</name> <amount1>100</amount1> <amount2>50</amount2> </worker> <worker> <name>Bob</name> <amount1>200</amount1> </worker> <worker> <name>Charlie</name> <amount2>75</amount2> </worker> </workers>
方案1:XSLT 2.0(推荐,语法更简洁)
如果你的处理器支持XSLT2.0(比如Saxon),直接用这个方案:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <!-- 预收集所有要生成的EX/ER行的简化信息,避免重复判断逻辑 --> <xsl:variable name="all-records"> <xsl:for-each select="/workers/worker"> <!-- 有amount1就生成EX类型的记录 --> <xsl:if test="amount1"> <record type="EX" amount="{amount1}"/> </xsl:if> <!-- 有amount2就生成ER类型的记录 --> <xsl:if test="amount2"> <record type="ER" amount="{amount2}"/> </xsl:if> </xsl:for-each> </xsl:variable> <xsl:template match="/"> <output> <!-- 统计头部:总行数、amount1总和、amount2总和 --> <summary> <total-rows><xsl:value-of select="count($all-records/record)"/></total-rows> <sum-amount1><xsl:value-of select="sum($all-records/record[@type='EX']/@amount)"/></sum-amount1> <sum-amount2><xsl:value-of select="sum($all-records/record[@type='ER']/@amount)"/></sum-amount2> </summary> <!-- 输出原来的EX/ER行内容 --> <details> <xsl:for-each select="/workers/worker"> <xsl:if test="amount1"> <EX> <worker-name><xsl:value-of select="name"/></worker-name> <amount><xsl:value-of select="amount1"/></amount> </EX> </xsl:if> <xsl:if test="amount2"> <ER> <worker-name><xsl:value-of select="name"/></worker-name> <amount><xsl:value-of select="amount2"/></amount> </ER> </xsl:if> </xsl:for-each> </details> </output> </xsl:template> </xsl:stylesheet>
关键逻辑说明:
$all-records变量:把所有要生成的EX/ER行转换成简化的<record>节点,这样统计时不用重复判断“有没有amount1/amount2”,避免逻辑冗余。count($all-records/record):直接算出EX+ER的总行数。sum($all-records/record[@type='EX']/@amount):筛选所有EX类型的记录,求和对应的amount值(也就是所有amount1的总和),同理处理amount2。
方案2:XSLT 1.0(兼容老处理器)
如果只能用XSLT1.0,需要借助扩展函数来处理变量中的节点集,代码如下:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl"> <xsl:output method="xml" indent="yes"/> <xsl:variable name="all-records"> <xsl:for-each select="/workers/worker"> <xsl:if test="amount1"> <record type="EX" amount="{amount1}"/> </xsl:if> <xsl:if test="amount2"> <record type="ER" amount="{amount2}"/> </xsl:if> </xsl:for-each> </xsl:variable> <xsl:template match="/"> <output> <summary> <total-rows><xsl:value-of select="count(exsl:node-set($all-records)/record)"/></total-rows> <sum-amount1> <xsl:call-template name="sum-records"> <xsl:with-param name="type" select="'EX'"/> </xsl:call-template> </sum-amount1> <sum-amount2> <xsl:call-template name="sum-records"> <xsl:with-param name="type" select="'ER'"/> </xsl:call-template> </sum-amount2> </summary> <details> <xsl:for-each select="/workers/worker"> <xsl:if test="amount1"> <EX> <worker-name><xsl:value-of select="name"/></worker-name> <amount><xsl:value-of select="amount1"/></amount> </EX> </xsl:if> <xsl:if test="amount2"> <ER> <worker-name><xsl:value-of select="name"/></worker-name> <amount><xsl:value-of select="amount2"/></amount> </ER> </xsl:if> </xsl:for-each> </details> </output> </xsl:template> <!-- XSLT1.0没有直接对属性求和的方便方法,自定义求和模板 --> <xsl:template name="sum-records"> <xsl:param name="type"/> <xsl:variable name="target-records" select="exsl:node-set($all-records)/record[@type=$type]"/> <xsl:choose> <xsl:when test="$target-records"> <xsl:value-of select="$target-records[1]/@amount + sum($target-records[position() > 1]/@amount)"/> </xsl:when> <xsl:otherwise>0</xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
关键注意点:
exsl:node-set($all-records):XSLT1.0中变量里的节点集默认不可查询,需要用这个扩展函数把它转换成可遍历的节点集(不同处理器可能用不同前缀,比如微软的msxsl:node-set)。- 自定义
sum-records模板:因为XSLT1.0的sum函数对属性节点集合的支持有限,所以通过迭代方式求和。
输出结果示例
用开头的示例XML,两种方案都会输出类似这样的结果:
<output> <summary> <total-rows>4</total-rows> <sum-amount1>300</sum-amount1> <sum-amount2>125</sum-amount2> </summary> <details> <EX> <worker-name>Alice</worker-name> <amount>100</amount> </EX> <ER> <worker-name>Alice</worker-name> <amount>50</amount> </ER> <EX> <worker-name>Bob</worker-name> <amount>200</amount> </EX> <ER> <worker-name>Charlie</worker-name> <amount>75</amount> </ER> </details> </output>
最后提醒
- 如果你的XML结构和示例不同,记得调整
select路径(比如worker节点的位置、amount字段的名称)。 - 确认你的XSLT处理器支持对应的版本,XSLT2.0需要用Saxon、Altova等工具,XSLT1.0是大多数浏览器和老工具默认支持的。
内容的提问来源于stack exchange,提问作者QnA




