ThomasM: XSLT 1.0 Sum

Beitrag lesen

Hallo Julian,

Es soll genau ein einziges "Amount" unter "AllowanceOrCharge_DEEE" mit dem Gesamtwert geben.

XSLT-1.0-Variante mit xsl:call-template:

<?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" version="1.0" encoding="UTF-8" standalone="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="AllowanceOrCharge_DEEE">
    <xsl:copy>
      <!--copy all other nodes-->
      <xsl:apply-templates select="@* | node()"/>
      <xsl:variable name="tx_anz" select="count(//Item[AllowanceOrCharge_Line[Code = 'TX']])"/>
      <xsl:if test="$tx_anz >= 1">
        <Amount>
          <xsl:call-template name="calc_amount">
            <xsl:with-param name="summe" select="0"/>
            <xsl:with-param name="i" select="1"/>
            <xsl:with-param name="max" select="$tx_anz"/>
          </xsl:call-template>
        </Amount>
      </xsl:if>
    </xsl:copy>
  </xsl:template>

  <!-- Identity-Template für die nicht explizit benannten Elemente -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template name="calc_amount">
    <xsl:param name="summe"/>
    <xsl:param name="i"/>
    <xsl:param name="max"/>

    <xsl:choose>
      <xsl:when test="$i &lt;= $max">
        <xsl:variable name="produkt" select="//Item[AllowanceOrCharge_Line[Code = 'TX']][$i]/TotalQuantity * //Item[AllowanceOrCharge_Line[Code = 'TX']][$i]/AllowanceOrCharge_Line/Amount"/>
        <xsl:call-template name="calc_amount">
          <xsl:with-param name="summe" select="$summe + $produkt"/>
          <xsl:with-param name="i" select="$i + 1"/>
          <xsl:with-param name="max" select="$max"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="format-number($summe, '#.00')"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

Mit XSLT 2.0/3.0 und for-in-return gestaltet es sich deutlich kompakter. Die einzelnen aufzusummierenden Werte werden darüber innerhalb von sum(…) zunächst vorberechnet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" standalone="yes" indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="AllowanceOrCharge_DEEE">
    <xsl:copy>
      <!--copy all other nodes-->
      <xsl:apply-templates select="@* | node()"/>
      <xsl:variable name="tx_anz" select="count(//Item[AllowanceOrCharge_Line[Code = 'TX']])"/>
      <xsl:if test="$tx_anz >= 1">
        <Amount>
          <xsl:value-of select="format-number(sum(for $item in //Item[AllowanceOrCharge_Line[Code = 'TX']]
            return $item/TotalQuantity * $item/AllowanceOrCharge_Line/Amount), '#.00')"/>
        </Amount>
      </xsl:if>
    </xsl:copy>
  </xsl:template>

  <!-- Identity-Template für die nicht explizit benannten Elemente -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Das Ergebnis ist jeweils:

[…]
<Totals>
  <AllowanceOrCharge_DEEE>
    <Code>TX</Code>
    <Amount>166.40</Amount>
  </AllowanceOrCharge_DEEE>
</Totals>
[…]

Grüße,
Thomas