2010-08-26 20 views
5

Ich bin neu bei Xsl-Transformationen und ich habe eine Frage. ich Schleife durch eine XML wie folgt aus:xsl Transformation

<PO> 
<Items> 
    <Item> 
    <Price>2</Price> 
    <Quantity>5</Quantity> 
    </Item> 
    <Item> 
    <Price>3</Price> 
    <Quantity>2</Quantity> 
    </Item>  
</Items> 
<QuantityTotal></QuantityTotal> 
</PO> 

Jetzt habe ich einen Wert im QuantityTotal Knoten eingefügt werden soll:
Der Wert ist die Summe der Preis * Menge aller Elemente, in diesem Fall (2 * 5) + (3 * 2) = 16 Wie kann ich das tun, ich habe es mit einer Schleife und Variablen versucht, aber Variablen sind unveränderlich, so weiß ich nicht, wie ich das erreichen kann.

Thx für Ihre Hilfe

+1

+1 gute Frage –

+0

Gute Frage (+1). Siehe meine Antwort für Lösungen in XSLT 1.0 (keine Erweiterungen erforderlich) und XSLT 2.0 –

+0

Siehe auch http://StackOverflow.com/questions/436998/multiply-2-numbers-and-then-sum-with-xslt und http ://Paketüberfluss.com/questions/1333558/xslt-to-sum-produkt-von-zwei-attribute – harpo

Antwort

4

Hier ist eine XSLT-Lösung funktioniert - keine Erweiterungsfunktionen erforderlich:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="node()|@*"> 
    <xsl:copy> 
     <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="QuantityTotal"> 
    <xsl:copy> 
    <xsl:call-template name="sumProducts"> 
    <xsl:with-param name="pNodes" select="../Items/Item"/> 
    </xsl:call-template> 
    </xsl:copy> 
</xsl:template> 

<xsl:template name="sumProducts"> 
    <xsl:param name="pNodes"/> 
    <xsl:param name="pSum" select="0"/> 
    <xsl:param name="pEname1" select="'Price'"/> 
    <xsl:param name="pEname2" select="'Quantity'"/> 

    <xsl:choose> 
    <xsl:when test="not($pNodes)"> 
    <xsl:value-of select="$pSum"/> 
    </xsl:when> 
    <xsl:otherwise> 
    <xsl:call-template name="sumProducts"> 
     <xsl:with-param name="pNodes" select= 
     "$pNodes[position() > 1]"/> 
     <xsl:with-param name="pSum" select= 
     "$pSum 
     + 
     $pNodes[1]/*[name()=$pEname1] 
     * 
     $pNodes[1]/*[name()=$pEname2] 
     "/> 
    </xsl:call-template> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 
</xsl:stylesheet> 

Wenn diese Transformation auf das bereitgestellte XML-Dokument angewendet wird:

<PO> 
    <Items> 
     <Item> 
      <Price>2</Price> 
      <Quantity>5</Quantity> 
     </Item> 
     <Item> 
      <Price>3</Price> 
      <Quantity>2</Quantity> 
     </Item> 
    </Items> 
    <QuantityTotal></QuantityTotal> 
</PO> 

das gewünschte Ergebnis wird produziert:

<PO> 
    <Items> 
     <Item> 
     <Price>2</Price> 
     <Quantity>5</Quantity> 
     </Item> 
     <Item> 
     <Price>3</Price> 
     <Quantity>2</Quantity> 
     </Item> 
    </Items> 
    <QuantityTotal>16</QuantityTotal> 
</PO> 
+0

+1 für gute XSLT 1.0 Lösung und XSLT 2.0 auch! –

1

Hier ist eine Lösung XSLT2 verwendet wird, in dem Knotenmengen First-Class-Objekte sind. In XSLT1 müssen Sie eine Knotensatzerweiterung verwenden.

Erklärung unten:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0"> 

    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:variable name="extendedItems" as="xs:integer*"> 
     <xsl:for-each select="//Item"> 
      <xsl:value-of select="./Price * ./Quantity"/> 
     </xsl:for-each> 
    </xsl:variable> 

    <xsl:variable name="total"> 
     <xsl:value-of select="sum($extendedItems)"/> 
    </xsl:variable> 

    <xsl:template match="//QuantityTotal"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xsl:value-of select="$total"/> 
     </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

Der hier Ansatz ist es, eine „Identität Transformation“ zu verwenden, um das Dokument zu kopieren, während der Berechnungen und das Ergebnis in die Ausgabe QuantityTotal Vorlage eingefügt wird. Die erste Vorlage kopiert die Eingabe in die Ausgabe, wird jedoch am Ende von einer spezifischeren Vorlage für "MengeTotal" überschrieben. Die erste Variablendeklaration erstellt eine Liste erweiterter Kosten und die zweite Variablendefinition summiert die Kosten, um die Summe zu erzeugen. Die Summe wird dann in den NumberTotal-Knoten eingefügt.

Der Schlüssel zum Verständnis von XSL ist, dass es deklarativer Natur ist. Der häufigste konzeptionelle Fehler, der von fast allen Anfängern gemacht wird, ist anzunehmen, dass das Stylesheet ein sequenzielles Programm ist, das das Eingabe-XML-Dokument verarbeitet. In Wirklichkeit ist es umgekehrt. Die XSL-Engine liest das XML-Dokument. und für jedes neue Tag, auf das es trifft, sucht es im Stylesheet nach der "besten" Übereinstimmung und führt diese Vorlage aus.

EDIT:

Hier ist eine xslt1.1 Version, die mit Saxon 6,5

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:ex="http://exslt.org/common" 
    extension-element-prefixes="ex" 
    version="1.1"> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:variable name="extendedItems"> 
     <xsl:for-each select="//Item"> 
      <extended> 
      <xsl:value-of select="./Price * ./Quantity"/> 
      </extended> 
      </xsl:for-each> 
    </xsl:variable> 
    <xsl:variable name="total"> 
     <xsl:value-of select="sum(ex:node-set($extendedItems/extended))"/> 
    </xsl:variable> 
    <xsl:template match="//QuantityTotal"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*"/> 
      <xsl:value-of select="$total"/> 
     </xsl:copy> 
    </xsl:template> 
</xsl:stylesheet> 
+0

danke für deine antwort, aber es funktioniert immer noch nicht wie es soll. Das Ergebnis, das ich jetzt erhalte, ist 106, was ist, ist es berechnet das erste Element (Ergebnis = 10) und das zweite Element (Ergebnis = 6) und so die Variable extendedItems seinen Wert wird "106". Auch wenn ich die sum() -Funktion für die Summe verwende, gibt es einen Fehler, dass ich einen Knoten-set() verwenden muss, so dass mein Code für die Summe wie folgt aussieht: sum (msxsl: node-set ($ extendedItems)). Was mache ich falsch? Thx im Voraus –

+0

Sie führen dies mit XSLT1. Haben Sie einen XSLT2-Transformator zur Verfügung? –

+0

Welchen XSL-Prozessor verwenden Sie? –

2

Neben Dimitre der ausgezeichneten Antwort, dieser Sheet nimmt anderen Ansatz:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:template match="node()|@*"> 
     <xsl:copy> 
      <xsl:apply-templates select="node()|@*"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="QuantityTotal"> 
     <xsl:copy> 
      <xsl:apply-templates select="../Items/Item[1]" mode="sum"/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="Item" mode="sum"> 
     <xsl:param name="pSum" select="0"/> 
     <xsl:variable name="vNext" select="following-sibling::Item[1]"/> 
     <xsl:variable name="vSum" select="$pSum + Price * Quantity"/> 
     <xsl:apply-templates select="$vNext" mode="sum"> 
      <xsl:with-param name="pSum" select="$vSum"/> 
     </xsl:apply-templates> 
     <xsl:if test="not($vNext)"> 
      <xsl:value-of select="$vSum"/> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 

Ausgang:

<PO> 
    <Items> 
     <Item> 
      <Price>2</Price> 
      <Quantity>5</Quantity> 
     </Item> 
     <Item> 
      <Price>3</Price> 
      <Quantity>2</Quantity> 
     </Item> 
    </Items> 
    <QuantityTotal>16</QuantityTotal> 
</PO> 
+0

@ Alejandro: Gute Antwort (+1). –

Verwandte Themen