2010-08-24 45 views
6

Me gustaría obtener nodos distintos de mi xml en múltiples niveles. ¿Alguien puede por favor darme algunas pistas de cómo hacer esto? Los métodos que busqué en Google (método Muenchian, for-each-group) se explicaron con claves de agrupación únicas y jerarquía simple.Nodos distintos en claves múltiples con XSLT

He aquí un ejemplo de mi 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> 

me gustaría tener nodos distintos persona basándose en el nombre y la edad, y también un conjunto distinto de correo-nodos. Por lo tanto, para el ejemplo, la salida deseada sería:

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

¿Hay alguna manera de hacerlo? Muchas gracias por adelantado.

+0

Se puede utilizar XSLT2 o está pegado con XSLT1? –

+0

@Jim: Como menciona 'for-each-group', creo que estamos seguros de usar XSLT 2.0. –

+0

XSLT 2 está bien. – Mork0075

Respuesta

11

I. XSLT 1.0 solución:

Esta transformación:

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

cuando se aplica en el documento XML proporcionado:

<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 el deseado, res correcto 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 solución

<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 Solución completa. Acerca de la plantilla 'mails': creo que' for-each-group/group' podría ser simplemente '". "'. He trabajado en una solución XSLT 2.0, pero era una plantilla de "ladrillo" ... –

+0

Muchas gracias, eso es todo. – Mork0075

Cuestiones relacionadas