¿Usando XPath para encontrar el primer hijo de un abuelo específico?

Estoy lidiando con un HTML retorcido. Aquí hay un ejemplo estilizado de la ascendencia de un elemento dado:

/html/body/foo/bar/baz/quux/b

Tengo una referencia a body en mi código y b es el elemento de contexto para mi consulta XPath. En el ejemplo anterior, ¿cómo encuentro foo? De manera más descriptiva, ¿cómo encuentro el primer elemento en una ascendencia dada que es hijo de otro elemento dado? Los elementos entre body y b son desconocidos y varían en tipo y profundidad en tiempo de ejecución. Puedo hacer esto fuera de XPath iterando sobre los ancestros de b hasta que llego a body pero me pregunto si hay algo de magia ancestral XPath para encontrar esta referencia relativa.

preguntado el 01 de julio de 12 a las 22:07

2 Respuestas

Utiliza:

ancestor::body[1]/foo

esto selecciona el elemento que es el primer elemento hijo de body ese es el primer antepasado body del nodo de contexto.

Incluso si sabes que no hay anidados body elementos, lo anterior es un poco más eficiente que:

ancestor::body/foo

porque evaluando esta última expresión, todos las antepasados ​​serán probados para ser un body.

ACTUALIZACIÓN:

En un comentario, el OP aclaró:

Bueno, estoy tratando de encontrar la intersección de los dos conjuntos (ancestros del nodo de contexto, hijos del cuerpo)

En XPath 1.0, esto es q sustitución directa en la conocida fórmula Kayessiana para la intersección de conjuntos de nodos:

$ns1[count(.|$ns2) = count($ns2)]

En este caso sustituimos $ns1 con ancestor::* y $ns2 con /*/body/*:

ancestor::*[count(. | /*/body/*) = count(/*/body/*)]

En XPath 2.0 esto es más fácil, usando el intersect operador:

ancestor::* intersect /*/body/*

Respondido 02 Jul 12, 06:07

Perdón por no ser claro: foo, bar, baz y quux son etiquetas ficticias. En el documento no se conoce la posición de foo entre los hijos del cuerpo y esto no funciona. ¿Tiene alguna sugerencia sobre cómo cambiar la redacción de la pregunta para ayudar a que esto quede más claro? - ldrg

Ack, lo siento, todavía no estaba claro. Ni siquiera sé el nombre de etiqueta de foo, podría ser cualquier cosa. Solo tengo body y el nodo de contexto y saber que el nodo de contexto es un descendiente de body en algún nivel (podría ser inmediato, podría estar anidado). - ldrg

Entonces, mientras buscaba en Google, encontré una de sus respuestas anteriores sobre este tema (stackoverflow.com/a/3591270/296239). He probado el xpath /html/body/*[count(. | ancestor-or-self::*) = count(ancestor-or-self::*)] y devuelve una lista igual a la de /html/body/*. Haciendo comprobaciones independientes en este xpath puedo ver que /html/body/* y ancestor-or-self::* están produciendo los juegos correctos. ¿Algún consejo de por qué no van juntos? - ldrg

@ldrg: si no conoce el nombre del elemento que desea seleccionar, ¿cómo define qué elemento debe seleccionarse? Esto está completamente ausente en la pregunta. ¿Sabes realmente lo que estás preguntando? Y la pregunta / respuesta vinculada desde su anterior. el comentario es sobre algo totalmente diferente: es cómo seleccionar un conjunto de nodos que se encuentran entre dos nodos dados, no lo que está preguntando en esta pregunta. - dimitre novachev

Bueno, estoy tratando de encontrar la intersección de los dos conjuntos (ancestros del nodo de contexto, hijos de body) que contiene lo que estoy buscando. Sé que está en el xml, sé que lógicamente es la intersección de los dos conjuntos, ¿hay alguna forma de obtenerlo a través de xpath puro? Tal vez simplemente redacté totalmente mal la pregunta. - ldrg

Sin probarlo, supongo que ancestor::body/*[1] podría hacerlo.

Explicación: ancestor::body es la primera body elemento a lo largo del ancestor 'eje', /* selecciona todos los hijos de ese elemento, y [1] selecciona el primer elemento de esa lista de hijos, que en este caso es foo.

Respondido 01 Jul 12, 22:07

Perdón por no ser claro: foo, bar, baz y quux son etiquetas ficticias. En el documento no se conoce la posición de foo entre los hijos del cuerpo y esto no funciona. ¿Tiene alguna sugerencia sobre cómo cambiar la redacción de la pregunta para ayudar a que esto quede más claro? - ldrg

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.