2012-04-07 44 views
7

He jugado con diferentes consultas XPath con XPather (sólo funciona con versiones anteriores de Firefox) y observa una diferencia entre los resultados de las consultas siguienteshacer una doble barra inclinada en XPath predicado funcionan igual que en el camino en sí

Ésta muestra algunos resultados

//div[descendant::table/descendant::td[4]] 

Ésta enumera lista vacía

//div[//table//td[4]] 

¿Son diferentes debido a algunas reglas o es sólo el mal comportamiento de particula ¿Implementación del intérprete XPath? (Que parece ser usado de motor FF, XPather es sólo una excelente interfaz gráfica de usuario sencilla para realizar consultas)

Respuesta

8

Con XPath 1.0 // es una abreviatura para /descendant-or-self::node()/ por lo que su primera ruta es /descendant-or-self::node()/div[descendant::table/descendant::td[4]] mientras que la segunda es bastante diferente con /descendant-or-self::node()/div[/descendant-or-self::node()/table/descendant-or-self::node()/td[4]]. Por lo tanto, la principal diferencia es que dentro de su primer predicado se buscan descendientes en relación con el elemento div, mientras que en el segundo predicado se buscan descendientes del nodo raíz / (también denominado nodo de documento). Es posible que desee //div[.//table//td[4]] para que la segunda expresión de ruta se acerque a la primera.

[editar] Este es un ejemplo:

<html> 
    <body> 
    <div> 
     <table> 
     <tbody> 
      <tr> 
      <td>1</td> 
      </tr> 
      <tr> 
      <td>2</td> 
      </tr> 
      <tr> 
      <td>3</td> 
      </tr> 
      <tr> 
      <td>4</td> 
      </tr> 
     </tbody> 
     </table> 
    </div> 
    </body> 
</html> 

Con que muestra la ruta de acceso //div[descendant::table/descendant::td[4]] selecciona el elemento div ya que tiene un niño table que tiene un cuarto td descendiente.

Sin embargo, con //div[.//table//td[4]] buscamos //div[./descendant-or-self::node()/table/descendant-or-self::node()/td[4]] que es corto para //div[./descendant-or-self::node()/table/descendant-or-self::node()/child::td[4]] y no hay ningún elemento que tiene un cuarto elemento td niño.

Espero que esto explique la diferencia, si usa //div[.//table/descendant::td[4]], entonces debería obtener el mismo resultado que con su forma original.

+1

Gracias, pero su versión arreglada tampoco funciona. Comprueba la URL de esta página con XPather/FF3.6.28 (disponible en PortableApps). La sintaxis completa devuelve 9 resultados, mientras que la breve con punto 0 resulta. ¿Hay algún otro (sitio/programa) con un motor diferente para verificar? – Maksee

+0

Considere publicar una muestra del XML o HTML que está consultando con XPath. Creo que he dado la explicación correcta sobre lo que '//' representa, también vea http://www.w3.org/TR/xpath/#path-abbrev. La larga sintaxis con 'descenddant-or-self :: node()' es difícil de leer y escribir, así que pude cometer dos errores al escribir las muestras de ruta, espero haberlas corregido ahora. Todavía hay una diferencia entre '[descenddant :: table/descenddant :: td [4]]' y '[.// table // td [4]]', lo mejor es hablar de eso con datos de muestra. –

+0

Los datos de muestra son esta página (lo siento si no estaba claro), por lo que el contenido de esta página estamos discutiendo en :) – Maksee

4

Hay una nota importante en el documento W3C sobre XPath:

XML Path Language (XPath) Version 1.0
    2 Location Paths
        2.5 Abbreviated Syntax

NOTA: la vía de ubicación //para[1] no significa que la misma como la ruta de ubicación /descendant::para[1]. Este último selecciona el primer elemento descendente para; el primero selecciona todos los descendientes para elementos que son los primeros para hijos de sus padres.

Eso significa que el doble barra dentro de la trayectoria no es sólo un atajo para /descendant-or-self::node()/ sino también un punto de partida para el siguiente nivel de una iteración árbol XML, lo que implica la expresión paso a la derecha de // se vuelve a ejecutar en cada descendiente del nodo de contexto actual.

Así que el significado exacto del predicado en este camino

//div[ descendant::table/descendant::td[4] ] 

es:

  • construir una secuencia de todos <table> nodos descendientes a la corriente <div>,
  • para cada uno de esos <table> acumulación una secuencia de todos los elementos descendentes <td>y concatenándolos en una única secuencia e,
  • filtra esa secuencia para su cuarto elemento.

Finalmente, la ruta devuelve todos los elementos <div> del documento, que tienen al menos cuatro celdas de datos en todas sus tablas anidadas. Y dado que hay tablas en el documento que tienen 4 celdas o más (incluidas las celdas de las tablas anidadas, por supuesto), la expresión completa selecciona sus respectivos antecesores <div>.

Por otro lado el predicado en

//div[ //table//td[4] ] 

significa:

  • escanear toda la estructura del documento para <table> elementos (más precisamente, probar el nodo raíz y el descendiente de cada raíz si tiene una <table> hijo),
  • para cada tabla encontrada escanee su subárbol para elementos que tienen un cuarto subelemento <td> (es decir, pruebe si la tabla o cualquiera de sus descendientes tiene al menos cuatro <td> niños).

Tenga en cuenta que la subexpresión del predicado no depende del nodo de contexto. Es una ruta global, que se resuelve en una secuencia de nodos (posiblemente vacía), por lo que el valor booleano del predicado depende solo de la estructura del documento. Si es verdadero, la ruta completa devuelve una secuencia de todos los elementos <div> en el documento, sino la secuencia vacía.

Finalmente el predicado sería cierto si y sólo si había un elemento en cualquier mesa, que tiene 4 (al menos) las celdas de datos.
Y hasta donde puedo ver todas las filas <tr> contienen dos o tres celdas - no hay elemento con 4 o más hijos <td>, por lo que la subexpresión del predicado devuelve una secuencia vacía, el predicado es falso y toda la ruta se filtra fuera. El resultado es: nada (secuencia vacía).

Cuestiones relacionadas