You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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

火山引擎 最新活动