2010-11-29 90 views
7

Digamos que estoy consultando un documento xhtml y quiero consultar a todos los hermanos que siguen una tabla con id='target'. Además, no quiero el primer hermano <table> ni el primer hermano <ol> de este elemento en particular. Aquí está lo mejor que pude encontrar:XPath - Todos los hermanos siguientes excepto los primeros elementos específicos

//table[@id='target']/following-sibling::*[not(self::table[1]) and not(self::ol[1])] 

Sin embargo, esto no está dando ningún resultado cuando debería. Obviamente, no estoy entendiendo algo de la sintaxis para esto (no pude encontrar una buena fuente de información). Ciertamente agradecería que alguien más experimentado con la sintaxis XPath pudiera echarme una mano. Además, con fines puramente académicos, me gustaría saber qué está haciendo realmente lo anterior.

ACTUALIZACIÓN:
Consulte la respuesta de LarsH para obtener una explicación de por qué mi XPath no funcionaba, y vea la respuesta de Dimitre para la solución aceptada.

+0

Buena pregunta, +1. Vea mi respuesta para una sola expresión XPath que selecciona exactamente los nodos deseados. :) –

Respuesta

10

Uso:

/table[@id='target']/following-sibling::*[not(self::table) and not(self::ol)] 
| 
/table[@id='target']/following-sibling::table[position() > 1] 
| 
/table[@id='target']/following-sibling::ol[position() > 1] 

Esto selecciona todos los siguientes hermanos de la tabla que no son table y no son ol y todos los siguientes table hermanos con posición 2 o mayor y todos los siguientes ol hermanos con posición 2 o mayor.

que es exactamente lo que quiere: todos los siguientes hermanos con la excepción de la primera table siguiente hermano y la primera ol siguientes hermanos.

Esto es puro XPath 1.0 y no utiliza ninguna función XSLT.

+1

Esta es exactamente la solución elegante que estaba buscando, y en realidad es bastante simple. Nunca se me ocurrió obtener la unión de los tres conjuntos mutuamente excluyentes que contienen todo excepto esos dos elementos. ¡Gracias! – Shaun

+0

@Shaun: De nada. –

+0

Ah, debería haberlo pensado más. :-) Estaba intentando una solución sindical pero no pude conseguirla. Sin embargo, de acuerdo con la pregunta original, '//' en lugar de '/' es necesario antes de 'table'. – LarsH

1

Solo habrá un nodo cuando use el eje self::, por lo que creo que self::*[1] siempre será verdadero. Cada nodo será el primer (y único) nodo en su propio eje self::. Esto significa que su expresión entre corchetes es equivalente a [not(self::table) and not(self::ol)], lo que significa que todas las tablas y listas se filtran.

no tengo un entorno de prueba establecido, pero fuera de la parte superior de la cabeza que esto podría hacer mejor:

/table[@id='target']/following-sibling::* 
    [not(self::table and not(preceding-sibling::table)) and 
    not(self::ol and not(preceding-sibling::ol))] 

lo necesitará algunos ajustes, pero la idea es filtrar las table s que no tienen hermanos precedentes table s, y ol s que no tienen hermanos precedentes ol s.

+0

como noté en mi respuesta, esta solución omite el hecho de que la tabla de objetivos no debe contar como hermano anterior cuando está probando a los hermanos siguientes para ver cuál es '[1]'. Además, cualquier tabla o ol elementos * antes * de la tabla de destino tienen que ser ignorados por la prueba anterior-hermano. – LarsH

2

Respondiendo a la segunda pregunta primero: lo que está haciendo arriba es seleccionar todos los hermanos siguientes que no son table ni ol elementos.

Aquí se explica por qué: self::table[1] selecciona el auto del nodo contextual (si pasa la prueba del nombre del elemento table) y los filtros para seleccionar solo el primer nodo a lo largo del eje self ::. Hay como máximo un nodo en el eje self :: que pasa la prueba del nombre del elemento, por lo que el [1] es redundante. self::table[1] selecciona el nodo de contexto siempre que sea un elemento de tabla, independientemente de su posición entre sus hermanos. Por lo tanto, not(self::table[1]) devuelve falso siempre que el nodo de contexto sea un elemento de tabla, independientemente de su posición entre los hermanos.

Similarmente para self::ol[1].

Cómo hacer lo que está tratando de hacer: @John La respuesta de Kugelman es casi correcta, pero omite el hecho de que debemos ignorar los elementos hermanos antes e incluyendo table[@id='target']. No creo que sea posible hacerlo correctamente en XPath 1.0 puro. ¿Tienes la posibilidad de utilizar XPath 2.0? Si está trabajando en un navegador, la respuesta es generalmente no.

Algunas soluciones serían:

  • Saltar el primer hermano siguiente tabla y la primera tras ol hermanos filtrando sobre alguna otra base, tales como sus atributos;
  • Seleccione //table[@id='target'] como un conjunto de nodos, regréselo al entorno host (es decir, fuera de XPath, p.en JavaScript), recorra ese conjunto de nodos; dentro del ciclo: seleccione following-sibling::* a través de XPath, recorra ese fuera de XPath, y pruebe cada resultado (fuera de XPath) para ver si es la primera tabla o ol.
  • Seleccione //table[@id='target'] como un conjunto de nodos, regréselo al entorno de host (es decir, fuera de XPath, por ejemplo, en JavaScript), recorra ese conjunto de nodos; dentro del ciclo: seleccione generate-id(following-sibling::table[1]) y generate-id(following-sibling::ol[1]) a través de XPath, reciba esos valores en las variables JS t1id y o1id, y construya una cadena para la expresión XPath usando el formulario 'following-sibling::*[generate-id() != ' + t1id + ' and generate-id() != ' + o1id + ']'. ¡Seleccione esa cadena en XPath y tendrá su respuesta! :-P

Actualización: Una solución es posible en XSLT 1.0 - ver @ Dimitre de.

+0

@John Kugelman: Echa un vistazo a mi amswer para obtener una solución pura y no demasiado compleja XPath 1.0. –

+0

Gracias por la excelente explicación de por qué mi intento de consulta estaba fallando. Finalmente fui con XPath de Dimitre para resolver el problema. – Shaun

Cuestiones relacionadas