2012-07-05 20 views
6

tengo el siguiente documento XML simple:filtro xquery en el atributo y el elemento

<?xml version="1.0" encoding="UTF-8"?> 
<cars> 
    <car> 
     <data attrib="Make"> 
      <text>Volvo</text> 
     </data> 
     <data attrib="Model"> 
      <text>855</text> 
     </data> 
    </car> 
    <car> 
     <data attrib="Make"> 
      <text>Volvo</text> 
     </data> 
     <data attrib="Model"> 
      <text>745</text> 
     </data> 
    </car> 
    <car> 
     <data attrib="Make"> 
      <text>Volvo</text> 
     </data> 
     <data attrib="Model"> 
      <text>V70R</text> 
     </data> 
    </car> 
</cars> 

y la siguiente XPath:

/cars/car/data[(@attrib='Model') and (text='855')] 

Esto devuelve el resultado siguiente:

<data attrib="Model"><text>855</text></data> 

I quiere que el XPath devuelva todo el bloque <car> para la coincidencia.

Así retorno de datos serían así:

<cars> 
    <car> 
     <data attrib="Make"> 
      <text>Volvo</text> 
     </data> 
     <data attrib="Model"> 
      <text>855</text> 
     </data> 
    </car> 
</cars> 

¿Cómo podría modificar la expresión XPath de arriba para lograr esto?

Respuesta

13

XPath devuelve cualquier nodo al que vaya, en su caso va al data, así que eso es lo que está obteniendo. Si quiere car, coloque su predicado después de car.

/cars/car[data/@attrib='Model' and data/text='855'] 

O, un poco más corto

/cars/car[data[@attrib='Model' and text='855']] 

se puede ejecutar en this XMLPlayground.

XQuery para producir la salida deseada:

<cars> 
    {/cars/car[data[@attrib='Model' and text='855']]} 
</cars> 
+2

Solo para agregar a esta respuesta: esto devolverá el elemento "automóvil" seleccionado. No envolverá esto en un elemento de "autos" según lo solicitado. Debido a que la entrada no contiene un elemento "autos" existente con un solo niño, solo puede lograr esta salida mediante la construcción de un nuevo elemento "autos", y para eso necesitará XQuery en lugar de XPath. –

+0

Para agregar al comentario de @MichaelKay, es aún más fácil y más natural hacer tal procesamiento con XSLT que con XQuery. –

+0

@Dimitre - Estoy de acuerdo – Utkanos

3

Aquí es una completa y probablemente una de las más cortas soluciones XSLT posibles:

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

<xsl:template match="/*"> 
    <cars> 
     <xsl:copy-of select="car[data[@attrib='Model' and text='855']]"/> 
    </cars> 
</xsl:template> 
</xsl:stylesheet> 

Sin embargo, la siguiente transformación, utilizando el bien conocido identity rule es más fácil de escribir y ofrece la máxima flexibilidad, extensibilidad y facilidad de mantenimiento:

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

    <xsl:template match="car[not(data[@attrib='Model' and text='855'])]"/> 
    </xsl:stylesheet> 

Cuando cualquiera de estas dos transformaciones se aplica en el documento XML proporcionado:

<cars> 
    <car> 
     <data attrib="Make"> 
      <text>Volvo</text> 
     </data> 
     <data attrib="Model"> 
      <text>855</text> 
     </data> 
    </car> 
    <car> 
     <data attrib="Make"> 
      <text>Volvo</text> 
     </data> 
     <data attrib="Model"> 
      <text>745</text> 
     </data> 
    </car> 
    <car> 
     <data attrib="Make"> 
      <text>Volvo</text> 
     </data> 
     <data attrib="Model"> 
      <text>V70R</text> 
     </data> 
    </car> 
</cars> 

se produce el resultado deseado, correcta:

<cars> 
    <car> 
     <data attrib="Make"> 
     <text>Volvo</text> 
     </data> 
     <data attrib="Model"> 
     <text>855</text> 
     </data> 
    </car> 
</cars> 

Explicación:

  1. La primera transformación genera el elemento superior cars, luego simplemente selecciona el elemento car deseado y lo copia como el cuerpo de cars.

  2. La segunda transformación se basa en uno de los patrones de diseño XSLT más fundamentales y potentes: el uso y la anulación de la regla de identidad.

  3. La plantilla de identidad copia cada nodo coincidente (para el que se selecciona para procesar) "tal cual".

  4. Hay una plantilla que anula la regla de identidad. Esta plantilla coincide con cualquier car para el que no sea cierto que data[@attrib='Model' and text='855']. El cuerpo de la plantilla está vacío y esto no genera nada del elemento coincidente car que se está copiando a la salida; en otras palabras, podemos decir que el elemento car que coincide con 0 se ha "eliminado".

Cuestiones relacionadas