2012-10-08 12 views
5

In che modo Muenchian grouping funziona in dettaglio?Applicazione del raggruppamento Muenchian per un XML semplice con XSLT

Ho un documento XML semplice generato da un database:

<CLIENTS> 
    <CLIENT> 
     <NAME>John</NAME> 
     <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER> 
     <LAST_USED>2012-10-03</LAST_USED> 
     <AMOUNT>5000</AMOUNT> 

    </CLIENT> 
    <CLIENT> 
     <NAME>John</NAME> 
     <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER> 
     <LAST_USED>2012-10-02</LAST_USED> 
     <AMOUNT>10000</AMOUNT> 
    </CLIENT> 
     ... 

mi piacerebbe gruppo dal nodo nome. Come posso ottenere l'output desiderato come segue?

<ClIENTS> 
    <CLIENT> 
     <NAME>John</NAME> 
     <ACCOUNT> 
      <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER> 
      <LAST_USED>2012-10-03</LAST_USED> 
      <AMOUNT>5000</AMOUNT> 
     </ACCOUNT> 
     <ACCOUNT> 
      <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER> 
      <LAST_USED>2012-10-03</LAST_USED> 
      <AMOUNT>10000</AMOUNT> 
     </ACCOUNT> 
     .... 
</CLIENTS> 

risposta

8

www.jenitennison.com/xslt/grouping/muenchian.xml Read, per un aiuto con il codice di definire una chiave

<xsl:key name="client-by-name" match="CLIENT" use="NAME"/> 

quindi utilizzare modelli come

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


<xsl:template match="CLIENTS"> 
    <xsl:copy> 
    <xsl:apply-templates select="CLIENT[generate-id() = generate-id(key('client-by-name', NAME)[1])]" mode="group"/> 
    <xsl:copy> 
</xsl:template> 

<xsl:template match="CLIENT" mode="group"> 
    <xsl:copy> 
    <xsl:copy-of select="NAME"/> 
    <xsl:apply-templates select="key('client-by-name', NAME)"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="CLIENT"> 
    <ACCOUNT> 
    <xsl:apply-templates select="node()[not(self::NAME)]"/> 
    </ACCOUNT> 
</xsl:template> 

[modifica] Se si desidera utilizzare XSLT 2.0 quindi di Ovviamente non hai bisogno del raggruppamento di Muenchian, invece usi

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

<xsl:template match="CLIENTS"> 
    <xsl:copy> 
    <xsl:for-each-group select="CLIENT" group-by="NAME"> 
     <CLIENT> 
     <xsl:apply-templates select="NAME, current-group()"/> 
     </CLIENT> 
    </xsl:for-each-group> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="CLIENT"> 
    <ACCOUNT> 
    <xsl:apply-templates select="node() except NAME"/> 
    </ACCOUNT> 
</xsl:template> 
+0

Mille grazie per le risposte utili ragazzi. È possibile risolvere lo stesso problema con xlslt 2.0 xls: for-each-group? Se sì, puoi postare la soluzione? – user1728778

+0

@ user1728778 - sì, questo è possibile. Si prega di vedere la mia risposta per una soluzione XSLT 2.0 compatta. – ABach

4

Jeni Tennison rompe i passaggi necessari per eseguire raggruppamento Muenchian qui:

http://www.jenitennison.com/xslt/grouping/muenchian.html

Essenzialmente, utilizzare XSLT per assegnare un tasto al nodo, questo tasto può essere ripetuto se c'è nodi identici nel documento. L'XSLT passa quindi attraverso ogni chiave e consente di generare i nodi con chiavi corrispondenti.

Così, in risposta di Martin, questa linea è la creazione di una chiave per ogni cliente in base al contenuto del nodo NOME (ricordo se il nome è lo stesso per più client, così sarà la chiave):

<xsl:key name="client-by-name" match="CLIENT" use="NAME"/> 

poi vuoi passare attraverso tutte le chiavi e trovare la prima istanza di ogni (sempre utilizzando la risposta di Martin)

<xsl:apply-templates select="CLIENT[generate-id() = generate-id(key('client-by-name', NAME)[1])]" mode="group"/> 

È quindi desidera trovare tutti i client che corrispondono la chiave per essere in grado di uscita la loro dettaglio (ancora, Martins)

<xsl:apply-templates select="key('client-by-name', NAME)"/> 

Da qui avresti bisogno un altro modello per l'output dei dati di clienti

3

Muenchian raggruppamento (come da @ risposta di Martin) elimina la ridondanza che una strategia di raggruppamento più tradizionale ha, mentre il raggruppamento.

Senza Muenchian raggruppamento modelli solitamente usato preceding-sibling o following-sibling per determinare la prima istanza candidato di ciascun gruppo, e quindi richiederebbe una seconda query di ricercare tutti i nodi corrispondenti al gruppo, come segue:

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

    <xsl:template match="CLIENTS"> 
     <CLIENTS> 
      <!--Only find the 'first' instance of each client--> 
      <xsl:apply-templates select="CLIENT[not(NAME = preceding-sibling::CLIENT/NAME)]" mode="client"/> 
     </CLIENTS> 
    </xsl:template> 

    <xsl:template match="CLIENT" mode="client"> 
     <xsl:variable name="name" select="NAME"/> 
     <CLIENT> 
      <NAME> 
       <xsl:value-of select="$name"/> 
      </NAME> 
      <ACCOUNTS> 
       <!--Note that we now have to find the other matching clients *again* - this is the inefficiency that Muenchian grouping eliminates--> 
       <xsl:apply-templates select="/CLIENTS/CLIENT[NAME/text()=$name]" mode="account" /> 
      </ACCOUNTS> 
     </CLIENT> 
    </xsl:template> 

    <xsl:template match="CLIENT" mode="account"> 
     <ACCOUNT> 
      <!--Copy everything else except Name, which is the grouping key --> 
      <xsl:copy-of select="@* | *[not(local-name='NAME')]"/> 
     </ACCOUNT> 
    </xsl:template> 

</xsl:stylesheet> 
+2

+1 per spiegare perché il raggruppamento dei muenchiani è addirittura necessario. – ABach

1

In un commento precedente (sotto la risposta di @ Martin), l'OP ha chiesto se l'elemento for-each-group di XSLT 2.0 potrebbe essere utilizzato per risolvere questo problema.

Quando questo XSLT 2.0 Soluzione:

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

    <xsl:template match="/*"> 
    <CLIENTS> 
     <xsl:for-each-group select="CLIENT" group-by="NAME"> 
     <CLIENT> 
      <xsl:sequence select="NAME" /> 
      <xsl:for-each select="current-group()"> 
      <ACCOUNT> 
       <xsl:sequence select="*[not(self::NAME)]" /> 
      </ACCOUNT> 
      </xsl:for-each> 
     </CLIENT> 
     </xsl:for-each-group> 
    </CLIENTS> 
    </xsl:template> 

</xsl:stylesheet> 

... viene applicato al XML originale del PO:

<CLIENTS> 
    <CLIENT> 
    <NAME>John</NAME> 
    <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER> 
    <LAST_USED>2012-10-03</LAST_USED> 
    <AMOUNT>5000</AMOUNT> 
    </CLIENT> 
    <CLIENT> 
    <NAME>John</NAME> 
    <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER> 
    <LAST_USED>2012-10-02</LAST_USED> 
    <AMOUNT>10000</AMOUNT> 
    </CLIENT> 
</CLIENTS> 

... il risultato voluto è prodotto:

<?xml version="1.0" encoding="utf-8"?> 
<CLIENTS> 
    <CLIENT> 
    <NAME>John</NAME> 
    <ACCOUNT> 
     <ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER> 
     <LAST_USED>2012-10-03</LAST_USED> 
     <AMOUNT>5000</AMOUNT> 
    </ACCOUNT> 
    <ACCOUNT> 
     <ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER> 
     <LAST_USED>2012-10-02</LAST_USED> 
     <AMOUNT>10000</AMOUNT> 
    </ACCOUNT> 
    </CLIENT> 
</CLIENTS> 

Spiegazione:

Come già giustamente ipotizzato, XSLT 2.0 ha introdotto l'elemento for-each-group (e i relativi partner associati, come ad esempio current-group()) al fine di eliminare le metodologie di raggruppamento sorprendenti/impressionanti, ma potenzialmente confuse come il Metodo Muenchian.