2010-08-24 5 views
6

Ich möchte verschiedene Knoten aus meinem XML auf mehreren Ebenen erhalten. Kann mir bitte jemand ein paar Hinweise geben, wie man das macht? Die Methoden, die ich googelte (Muenchsche Methode, für jede Gruppe), wurden mit einzelnen Gruppierungsschlüsseln und einfacher Hierarchie erklärt.Deutliche Knoten auf mehreren Schlüsseln mit XSLT

Hier ist ein Beispiel meiner 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> 

Ich mag würde eigenständige Person Knoten auf Namen und das Alter der Basis haben, und auch eine unterschiedliche Menge von E-Mail-Knoten. Für das Beispiel wäre die gewünschte Ausgabe:

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

Gibt es eine Möglichkeit, dies zu tun? Vielen Dank im Voraus.

+0

Können Sie XSLT2 oder sind Sie mit XSLT1 stecken? –

+0

@Jim: Da er 'for-each-group' erwähnt, können wir sicher XSLT 2.0 verwenden. –

+0

XSLT 2 ist in Ordnung. – Mork0075

Antwort

11

I. XSLT 1.0 Lösung:

Diese Transformation:

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

wenn auf der mitgelieferten XML-Dokument angewendet:

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

die gewünschte erzeugt, korrekte res 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. XSLT 2.0-Lösung

<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 Beatifull Lösung. Über die "Mails" Vorlage: Ich denke, dass "für jede Gruppe/Gruppe" nur "." Sein könnte. Ich habe in einer XSLT 2.0 Lösung gearbeitet, aber es war so ein "Ziegelstein" Vorlage ... –

+0

Vielen Dank, das ist es. – Mork0075