2010-08-26 15 views
5

Sono nuovo alle trasformazioni xsl e ho una domanda. Sto scorrendo un XML come questo:trasformazione xsl

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

Ora voglio inserire un valore nel nodo QuantityTotal:
Il valore è la somma del prezzo * quantità di tutti gli elementi, in questo caso (2 * 5) + (3 * 2) = 16 Come posso fare questo, l'ho provato con un ciclo e variabili, ma le variabili sono immutabili quindi non so come posso ottenerlo.

Thx per il vostro aiuto

+1

+1 bella domanda –

+0

Buona domanda (+1). Vedere la mia risposta per le soluzioni sia in XSLT 1.0 (senza estensioni necessarie), sia XSLT 2.0 –

+0

Vedere anche http://stackoverflow.com/questions/436998/multiply-2-numbers-and-then-sum-with-xslt e http : // StackOverflow.it/questions/1333558/xslt-to-sum-product-of-two-attributes – harpo

risposta

4

Ecco una soluzione XSLT - nessun funzioni di estensione necessari:

<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> 

quando questa trasformazione viene applicata sul documento XML fornito:

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

il risultato voluto è prodotto:

<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 per la buona soluzione XSLT 1.0 e XSLT 2.0! –

1

Ecco una soluzione che utilizza XSLT2, nel quale nodo-set sono oggetti di prima classe. In XSLT1 avresti bisogno di usare un'estensione set-nodo.

Spiegazione di seguito:

<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> 

L'approccio è quello di utilizzare un "Identità trasformare" per copiare il documento, durante l'esecuzione dei calcoli e inserire il risultato in uscita modello QuantityTotal. Il primo modello copia l'input nell'output ma viene sovrascritto da un modello più specifico per QuantityTotal nella parte inferiore. La prima dichiarazione di variabile crea un elenco di costi estesi e la seconda definizione di variabile somma i costi per produrre il totale. Il totale viene quindi inserito nel nodo Quantità totale.

La chiave per comprendere XSL è che è dichiarativa in natura. L'errore concettuale più comune fatto da quasi tutti i principianti è quello di assumere che il foglio di stile sia un programma sequenziale che elabora il documento XML di input. In realtà è il contrario. Il motore XSL legge il documento XML. e per ogni nuovo tag che incontra, appare nel foglio di stile per la corrispondenza "migliore", eseguendo quel modello.

EDIT:

Ecco una versione che funziona con xslt1.1 Sassone 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

grazie per la risposta, ma ancora non funziona come dovrebbe. Il risultato che ottengo ora è 106, quello che fa è calcolare il primo elemento (risultato = 10) e il secondo elemento (risultato = 6) e quindi la variabile extendedItems il suo valore diventa "106". Anche quando uso la funzione sum() per il totale, genera un errore che devo usare un node-set() così il mio codice per il totale diventa così: sum (msxsl: node-set ($ extendedItems)). Che cosa sto facendo di sbagliato? Thx in anticipo –

+0

Si sta eseguendo questo con XSLT1. Avete un trasformatore XSLT2 disponibile? –

+0

Quale processore XSL stai usando? –

2

Oltre risposta eccellente di Dimitre, questo foglio di stile richiede altro approccio:

<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> 

uscita:

<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: buona risposta (+1). –

Problemi correlati