2010-05-04 14 views
14

Per ogni nodo "agenzia" ho bisogno di trovare gli elementi "stmt" che hanno gli stessi valori key1, key2, key3 e generare un solo nodo "stmt" con "comm" e valori "prem" sommati. Per ogni elemento "stmt" all'interno di quella "agenzia" che non corrisponde ad altri elementi "stmt" basati su key1, key2 e key3, ho bisogno di renderli come sono. Quindi, dopo la trasformazione, il primo nodo "agency" avrebbe solo due nodi "stmt" (uno sommato) e il secondo nodo "agency" sarebbe passato così come è perché le chiavi non corrispondono. Le soluzioni XSLT 1.0 o 2.0 sono ok ... anche se il mio foglio di stile è attualmente 1.0. Si noti che i nodi dell'agenzia potrebbero avere un numero qualsiasi di elementi "stmt" con chiavi corrispondenti che devono essere raggruppate e sommate e qualsiasi numero che non lo fa.Come raggruppare e sommare i valori in XSLT

<statement> 
<agency> 
    <stmt> 
     <key1>1234</key1> 
     <key2>ABC</key2> 
     <key3>15.000</key3> 
     <comm>75.00</comm> 
     <prem>100.00</prem> 
    </stmt> 
    <stmt> 
     <key1>1234</key1> 
     <key2>ABC</key2> 
     <key3>15.000</key3> 
     <comm>25.00</comm> 
     <prem>200.00</prem> 
    </stmt> 
    <stmt> 
     <key1>1234</key1> 
     <key2>ABC</key2> 
     <key3>17.50</key3> 
     <comm>25.00</comm> 
     <prem>100.00</prem> 
    </stmt> 
</agency> 
<agency> 
    <stmt> 
     <key1>5678</key1> 
     <key2>DEF</key2> 
     <key3>15.000</key3> 
     <comm>10.00</comm> 
     <prem>20.00</prem> 
    </stmt> 
    <stmt> 
     <key1>5678</key1> 
     <key2>DEF</key2> 
     <key3>17.000</key3> 
     <comm>15.00</comm> 
     <prem>12.00</prem> 
    </stmt> 
</agency> 

+0

Buona domanda (+1). Vedi la mia risposta per una soluzione XSLT 1.0 completa. –

risposta

10

E un XSLT 2.0 soluzione:

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema" 
exclude-result-prefixes="xs" 
> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

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

<xsl:template match="agency"> 
    <agency> 
    <xsl:for-each-group select="stmt" group-by= 
    "concat(key1, '+', key2, '+', key3)"> 

    <stmt> 
     <xsl:copy-of select= 
     "current-group()[1]/*[starts-with(name(),'key')]"/> 

     <comm> 
     <xsl:value-of select="sum(current-group()/comm)"/> 
     </comm> 
     <prem> 
     <xsl:value-of select="sum(current-group()/prem)"/> 
     </prem> 
    </stmt> 
    </xsl:for-each-group> 
    </agency> 
</xsl:template> 
</xsl:stylesheet> 
+0

Il 'concat (key1, key2, key3)' fallirà in alcuni casi, ad esempio 'key1 =" 1A "key2 =" B "key3 =" 1.000 "' e 'key1 =" 1 "key2 =" AB "key3 =" 1.000 "' ... Sento che concatenare stringhe senza conoscenza intima del loro contenuto (o limitazione di ciò) è sbagliato. – Lucero

+0

@Lucero: Grazie ancora, non c'è niente di sbagliato con il concat - è stato qualcosa che mi è sfuggito - mi sento così assonnato per tutto il giorno oggi - che ora è corretto. Per favore, fammi sapere se la correzione ti soddisfa. Questa correzione è qualcosa di tipico in questo tipo di soluzioni. –

+0

@Dimitre, in contrasto con l'altro problema di 'concat',' + 'non è un separatore adatto qui, poiché i dati XML possono teoricamente molto bene avere stringhe di chiavi con' + 'in essi - pensa a' key1 = "1 + "key2 =" 2 "' e 'key1 =" 1 "key2 =" + 2 "'.Quindi il mio dire è che dovresti concatenarti solo quando sai che il separatore non farà mai parte dei dati concatenati. – Lucero

7

In XSLT 1.0 impiegare il metodo Muenchian per il raggruppamento (con chiave composta).

Questa trasformazione:

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

<xsl:key name="kStmtByKeys" match="stmt" 
     use="concat(generate-id(..), key1, '+', key2, '+', key3)"/> 

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

<xsl:template match="agency"> 
    <agency> 
    <xsl:for-each select= 
    "stmt[generate-id() 
      = 
      generate-id(key('kStmtByKeys', 
          concat(generate-id(..), key1, '+', key2, '+', key3) 
          )[1] 
         ) 
      ] 
    "> 
     <xsl:variable name="vkeyGroup" select= 
     "key('kStmtByKeys', concat(generate-id(..), key1, '+', key2, '+', key3))"/> 

    <stmt> 
     <xsl:copy-of select="*[starts-with(name(), 'key')]"/> 
     <comm> 
     <xsl:value-of select="sum($vkeyGroup/comm)"/> 
     </comm> 
     <prem> 
     <xsl:value-of select="sum($vkeyGroup/prem)"/> 
     </prem> 
    </stmt> 
    </xsl:for-each> 
    </agency> 
</xsl:template> 
</xsl:stylesheet> 

se applicato sul documento XML fornito, produce il risultato voluto:

<statement> 
    <agency> 
     <stmt> 
      <key1>1234</key1> 
      <key2>ABC</key2> 
      <key3>15.000</key3> 
      <comm>100</comm> 
      <prem>300</prem> 
     </stmt> 
     <stmt> 
      <key1>1234</key1> 
      <key2>ABC</key2> 
      <key3>17.50</key3> 
      <comm>25</comm> 
      <prem>100</prem> 
     </stmt> 
    </agency> 
    <agency> 
     <stmt> 
      <key1>5678</key1> 
      <key2>DEF</key2> 
      <key3>15.000</key3> 
      <comm>10</comm> 
      <prem>20</prem> 
     </stmt> 
     <stmt> 
      <key1>5678</key1> 
      <key2>DEF</key2> 
      <key3>17.000</key3> 
      <comm>15</comm> 
      <prem>12</prem> 
     </stmt> 
    </agency> 
</statement> 
+0

Se ho capito bene la domanda, la soluzione è rotta quando un'altra agenzia ha nodi 'stmt' con le stesse chiavi. A me sembra che dal momento che ci sono più agenzie, il metodo muenchiano con la chiave globale non funzionerà. – Lucero

+0

@Lucero: una buona osservazione, grazie. Questo è ora corretto e sto ancora usando il metodo Muenchian con un tasto compond. –

+0

Hm, è questo modo di generare una chiave composta garantita per dare i risultati desiderati in tutte le situazioni? se una chiave era 'concat ('1', '23')' e un'altra era 'concat ('12 ',' 3 ')' (si ottiene l'idea) questo potrebbe causare problemi a seconda del documento di input e del processore XSLT . – Lucero

1
<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"> 
    <xsl:output method="xml" indent="yes"/> 

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

    <xsl:template match="stmt"> 
     <xsl:variable name="stmtGroup" select="../stmt[(key1=current()/key1) and (key2=current()/key2) and (key3=current()/key3)]" /> 
     <xsl:if test="generate-id()=generate-id($stmtGroup[1])"> 
      <xsl:copy> 
       <key1> 
        <xsl:value-of select="key1"/> 
       </key1> 
       <key2> 
        <xsl:value-of select="key2"/> 
       </key2> 
       <key3> 
        <xsl:value-of select="key3"/> 
       </key3> 
       <comm> 
        <xsl:value-of select="format-number(sum($stmtGroup/comm), '#.00')"/> 
       </comm> 
       <prem> 
        <xsl:value-of select="format-number(sum($stmtGroup/prem), '#.00')"/> 
       </prem> 
      </xsl:copy> 
     </xsl:if> 
    </xsl:template> 
</xsl:stylesheet> 
Problemi correlati