2010-08-24 11 views
6

Vorrei ottenere nodi distinti dal mio xml su più livelli. Qualcuno può darmi qualche suggerimento su come farlo? I metodi i googled (metodo di Muenchian, per-ogni-gruppo) sono stati spiegati con chiavi di raggruppamento singolo e semplice gerarchia.Nodi distinti su più chiavi con XSLT

Ecco un esempio del mio xml:

<persons> 
<person> 
    <name>Tom</name> 
    <age>20</age> 
    <mails> 
    <mail>[email protected]</mail> 
    <mail>[email protected]</mail> 
    </mails> 
</person> 
<person> 
    <name>Tom</name> 
    <age>20</age> 
    <mails> 
    <mail>[email protected]</mail> 
    <mail>[email protected]</mail> 
    </mails> 
</person> 
</persons> 

Vorrei avere diversi nodi persona in base al nome e l'età, e anche una serie distinta di posta-nodi. Quindi per l'esempio l'output desiderato sarebbe:

<persons> 
<person> 
    <name>Tom</name> 
    <age>20</age> 
    <mails> 
    <mail>[email protected]</mail> 
    <mail>[email protected]</mail> 
    <mail>[email protected]</mail> 
    </mails> 
</person> 
</persons> 

C'è un modo per farlo? Grazie mille in anticipo.

+0

È possibile utilizzare XSLT2 o si è bloccati con XSLT1? –

+0

@ Jim: Poiché menziona 'for-each-group', suppongo che siamo sicuri di usare XSLT 2.0. –

+0

XSLT 2 va bene. – Mork0075

risposta

11

I. XSLT 1.0 soluzione:

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:strip-space elements="*"/> 

<xsl:key name="kPersByNameAndAge" match="person" 
    use="concat(name, '+', age)"/> 

<xsl:key name="kmailByNameAndAge" match="mail" 
    use="concat(../../name, '+', ../../age)"/> 

<xsl:key name="kmailByNameAndAgeAndVal" match="mail" 
    use="concat(../../name, '+', ../../age, '+', .)"/> 

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

<xsl:template match="/*"> 
    <persons> 
    <xsl:apply-templates select= 
    "person[generate-id() 
      = 
      generate-id(key('kPersByNameAndAge', 
          concat(name, '+', age) 
         ) 
          [1] 
        ) 
      ] 
    "/> 
    </persons> 
</xsl:template> 

<xsl:template match="mails"> 
    <mails> 
    <xsl:apply-templates select= 
    "key('kmailByNameAndAge', concat(../name, '+', ../age)) 
     [generate-id() 
     = 
     generate-id(key('kmailByNameAndAgeAndVal', 
         concat(../../name, '+', ../../age, '+', .) 
         ) 
          [1] 
        ) 
     ] 
    "/> 
    </mails> 
</xsl:template> 
</xsl:stylesheet> 

quando applicato sul documento XML fornito:

<persons> 
<person> 
    <name>Tom</name> 
    <age>20</age> 
    <mails> 
    <mail>[email protected]</mail> 
    <mail>[email protected]</mail> 
    </mails> 
</person> 
<person> 
    <name>Tom</name> 
    <age>20</age> 
    <mails> 
    <mail>[email protected]</mail> 
    <mail>[email protected]</mail> 
    </mails> 
</person> 
</persons> 

produce il desiderato, ris corretto ULT:

<persons> 
    <person> 
     <name>Tom</name> 
     <age>20</age> 
     <mails> 
      <mail>[email protected]</mail> 
      <mail>[email protected]</mail> 
      <mail>[email protected]</mail> 
     </mails> 
    </person> 
</persons> 

II. Soluzione XSLT 2.0

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

<xsl:key name="kmailByNameAndAge" match="mail" 
    use="concat(../../name, '+', ../../age)"/> 

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

<xsl:template match="/*"> 
    <persons> 
    <xsl:for-each-group select="person" group-by="concat(name, '+', age)"> 
    <xsl:apply-templates select="."/> 
    </xsl:for-each-group> 
    </persons> 
</xsl:template> 

<xsl:template match="mails"> 
    <mails> 
    <xsl:for-each-group select= 
    "key('kmailByNameAndAge', concat(../name, '+', ../age))" 
    group-by="concat(../../name, '+', ../../age, '+', .)" 
    > 
    <xsl:apply-templates select="."/> 
    </xsl:for-each-group> 
    </mails> 
</xsl:template> 
</xsl:stylesheet> 
+0

+1 Bella soluzione. Sul modello 'mails': Penso che' for-each-group/group' potrebbe essere semplicemente '". "'. Ho lavorato su una soluzione XSLT 2.0 ma era un modello "mattone" ... –

+0

Grazie mille, è tutto. – Mork0075