2010-10-20 8 views
8

Digamos que tengo un documento XML como esto:mediante XSLT apply-templates de forma condicional seleccione los nodos

<director> 
    <play> 
     <t>Nutcracker</t> 
     <a>Tom Cruise</a> 
    </play> 
    <play> 
     <t>Nutcracker</t> 
     <a>Robin Williams</a> 
    </play> 
    <play> 
     <t>Grinch Stole Christmas</t> 
     <a>Will Smith</a> 
    </play> 
    <play> 
     <t>Grinch Stole Christmas</t> 
     <a>Mel Gibson</a> 
    </play> 
</director> 

Ahora quiero ser capaz de seleccionar todas las jugadas con Will Smith como actor y cambiar el formato en algo como esto:

<Plays> 
    <Play title="Grinch Stole Christmas"> 
     <star>Will Smith</star> 
     <star>Mel Gibson</star> 
    </Play> 
</Plays> 

yo sólo quiero usar apply-templates .. Sin XSL: si o para cada bucles (he ideado este ejemplo como una versión más simple de lo que estoy haciendo lo que me puede ayudar entender cómo usar xpath dentro de una declaración de coincidencia)

Esto es lo que tengo hasta ahora:

<?xml version="1.0"?> 
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="xml" indent="yes" /> 
     <xsl:template match="/director"> 
       <Plays> 
       <xsl:apply-templates select="play"/> 
       </Plays> 
     </xsl:template> 

     <xsl:template match="play[a='Will Smith']"> 
       <play title="{data(t)[1]}"> 
       <xsl:apply-templates select="a"/> 
       </play> 
     </xsl:template> 

     <xsl:template match="a"> 
       <star> 
       <xsl:value-of select="."/> 
       </star> 
     </xsl:template> 
</xsl:stylesheet> 

Básicamente sólo soy seguro de cómo filtrar los nodos utilizando XPath en el atributo partido de la plantilla. ¡Cualquier ayuda sería genial!

+0

¿Qué significa lo que has hecho hasta ahora? –

+0

La siguiente solución lo resolvió ... Gracias por comentar al menos :) – Msencenb

+0

Buena pregunta, +1. Vea mi respuesta para dos soluciones eficientes :) –

Respuesta

7

La condición debe estar en xsl: apply-templates en lugar de xsl: template:

<Plays> 
    <xsl:apply-templates select="play[a='Will Smith']">"/> 
</Plays> 

En su solución, se están transformando TODAS <juego> nodos. Para los nodos de juego que coinciden con la condición, se aplica su plantilla. Pero para aquellos que no coinciden con la condición, se aplica una plantilla predeterminada ("transformación de identidad").

Como alternativa, puede mantener la condición de xsl: template match, además de añadir otra plantilla para <jugar> que no coinciden con la condición, para transformar los <juego> en la nada:

<xsl:template match="play[a='Will Smith']"> 
     <play title="{data(t)[1]}"> 
     <xsl:apply-templates select="a"/> 
     </play> 
    </xsl:template> 

    <xsl:template match="play"> 
    </xsl:template> 
+0

¡Gracias! Esto funciona como un encanto ahora – Msencenb

+0

@Msencenb: Esto no genera el resultado deseado. –

+0

¿Estás seguro? El primer ejemplo de código dado parece funcionar para mí – Msencenb

1

Esta hoja de estilo :

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:key name="kActorByTitle" match="a" use="../t"/> 
    <xsl:param name="pActor" select="'Will Smith'"/> 
    <xsl:template match="/"> 
     <Plays> 
      <xsl:apply-templates select="*/play[a=$pActor]"/> 
     </Plays> 
    </xsl:template> 
    <xsl:template match="play"> 
     <Play title="{t}"> 
      <xsl:apply-templates select="key('kActorByTitle',t)"/> 
     </Play> 
    </xsl:template> 
    <xsl:template match="a"> 
     <star> 
      <xsl:value-of select="."/> 
     </star> 
    </xsl:template> 
</xsl:stylesheet> 

salida:

<Plays> 
    <Play title="Grinch Stole Christmas"> 
     <star>Will Smith</star> 
     <star>Mel Gibson</star> 
    </Play> 
</Plays> 
+0

Esto también funciona bien y usa claves como otra respuesta. Creo que la clave para mí es que las condiciones deben estar dentro de la declaración de selección en lugar de la declaración de coincidencia – Msencenb

+0

@Msencenb: la coincidencia de patrones impulsada vs. la selección impulsada es una cuestión de estilo pull versus push. Ambos estan bien. En este caso, como uso un parámetro en una hoja de estilos XSLT 1.0, tuve que ir con un estilo push (las referencias param/var no están permitidas en los patrones XSLT 1.0) –

+0

@Alejandro, @@ Msencenb: las referencias de variables/parámetros son permitido en el patrón de coincidencia en XSLT 2.0 –

5

I. Probablemente la solución más eficiente XSLT 1.0:

<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="kWSPlayByTitle" match="play[a='Will Smith']" 
    use="t"/> 

<xsl:key name="kActorByTitle" match="a" 
    use="../t"/> 

<xsl:template match="/"> 
    <Plays> 
    <xsl:apply-templates select= 
    "*/play[generate-id() 
      = 
      generate-id(key('kWSPlayByTitle',t)[1]) 
      ]"/> 
    </Plays> 
</xsl:template> 

<xsl:template match="play"> 
    <Play title="{t}"> 
    <xsl:apply-templates select="key('kActorByTitle',t)"/> 
    </Play> 
</xsl:template> 

<xsl:template match="a"> 
    <star><xsl:value-of select="."/></star> 
</xsl:template> 
</xsl:stylesheet> 

cuando se aplica esta transformación en el documento XML proporcionado:

<director> 
    <play> 
     <t>Nutcracker</t> 
     <a>Tom Cruise</a> 
    </play> 
    <play> 
     <t>Nutcracker</t> 
     <a>Robin Williams</a> 
    </play> 
    <play> 
     <t>Grinch Stole Christmas</t> 
     <a>Will Smith</a> 
    </play> 
    <play> 
     <t>Grinch Stole Christmas</t> 
     <a>Mel Gibson</a> 
    </play> 
</director> 

el resultado deseado se produce:

<Plays> 
    <Play title="Grinch Stole Christmas"> 
     <star>Will Smith</star> 
     <star>Mel Gibson</star> 
    </Play> 
</Plays> 

hacer la nota:

  1. La eficiencia se logra mediante el uso de teclas tanto para todas las jugadas en las que Mel Gibson participó y para todos los actores que participaron en un dado (titulado) jugar.

  2. Incluso si un título de juego con Mel Gibson se aparece más de una vez (debido a un error accidental, tal vez ...) aparecerá solo una vez en el resultado.

II. Una solución simple y eficiente 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:template match="/*"> 
    <Plays> 
    <xsl:for-each-group select="play[a='Mel Gibson']" 
      group-by="t"> 
     <xsl:apply-templates select="."/> 
    </xsl:for-each-group> 
    </Plays> 
</xsl:template> 

<xsl:template match="play"> 
    <Play title="{t}"> 
    <xsl:for-each-group select="../play[t = current()/t]/a" 
     group-by="."> 
    <xsl:apply-templates select="."/> 
    </xsl:for-each-group> 
    </Play> 
</xsl:template> 

<xsl:template match="a"> 
    <star> 
    <xsl:value-of select="."/> 
    </star> 
</xsl:template> 
</xsl:stylesheet> 
+0

Nunca había oído hablar de llaves ... pero parece ser la más eficiente. Gracias – Msencenb

+0

@Msencenb: Tenga en cuenta que la solución XSLT 2.0 no utiliza claves en absoluto. Su rol es jugado por ''. Pero, por supuesto, incluso en XSLT 2.0 no hace daño, y de hecho puede ser útil, saber cómo usar las teclas. –

+0

@Dimitre: No fui con dos teclas porque esta expresión '" */play [generate-id() ...] 'repetirá todos los elementos' play' de todos modos. –

Cuestiones relacionadas