2009-05-21 21 views
9

He mirado en esta pregunta: Select first instance only with XPath?Seleccionar primer resultado en XPath

Pero si tengo un conjunto de nodos de esta manera:


<container value=""> 
    <data id="1"></data> 
    <data id="2">test</data> 
    <container> 
    <data id="1">test</data> 
    <data id="3">test</data> 
    </container> 
</container> 

Ahora mi escenario es que este conjunto de nodos es profunda dentro de un documento, y tengo un puntero al contenedor interno. Así que tengo que prefijar mi XPath con "/ contenedor/contenedor" (la ruta es en realidad más larga, pero para este ejemplo debería hacerlo).

EDIT: Lo que quiero es un nodo de "datos" con una identificación de 1, que debe provenir del nodo más bajo primero o el antecesor más cercano. Entonces, si no puedo encontrarlo en la "corriente" (/ contenedor/contenedor) debería mirar a los antepasados ​​y obtener el más cercano (o al final, no encontrar nada). He intentado esto:

/contenedor/envase/ancestro-o-self :: contenedor/datos [@ id = "1"]

¿Qué trae de nuevo un conjunto de resultados con dos nodos. Pensé que podría usar last() para obtener el más profundo, así que lo vi al final, pero fue en vano.

+0

La especificación podría hacer con más claridad. ¿Quieres el nodo de datos? El nodo de datos debe tener una identificación de 1? Forme el contenedor original si el nodo de datos no tiene una ID de 1 que desea mover hacia arriba del documento a un contenedor principal para ver si tiene un nodo de datos con una ID de 1, si no continúa hasta el nodo de datos con una ID de 1 se encuentra. ¿Eso lo describe? – AnthonyWJones

+0

Traté de describir lo que estaba buscando un poco más claro, aunque algunos lo adivinaron. ¡Lo siento! –

Respuesta

5

¿Cómo trató de virar última() en? Creo que esto debería funcionar:

/container/container/ancestor-or-self::container/data[@id="1"][last()] 

EDIT:

derecho, por supuesto, se me olvidó el paréntesis:

(/container/container/ancestor-or-self::container/data[@id="1"])[last()] 

cual hace que esta lo mismo que una de las otras respuestas; sin embargo, como el cartel original señala, esta expresión no funciona en:

<container value=""> 
    <container> 
    <data id="1">a3</data> 
    <data id="3">a4</data> 
    </container> 
    <data id="1">a1</data> 
    <data id="2">a2</data> 
</container> 

Fiel al espíritu de stackoverflow, que sin embargo puede combinar mi respuesta con una de las otras respuestas y obtener algo que funciona en todos los casos:

(/container/container/ancestor-or-self::container[data[@id="1"]])[last()]/data[@id="1"] 

Por cierto, si hay múltiples @ id-es-1 < datos> hijos de un mismo contenedor, esto devolverá todos ellos.Para devolver sólo el primero de esos> elemento < datos:

(/container/container/ancestor-or-self::container[data[@id="1"]])[last()]/data[@id="1"][1] 
+0

Si mi lectura aquí sobre el resultado deseado es correcta (grande si) esto no funcionará porque el último() coincidirá en ambas ramas del ancestro o el yo: esa es la problema real, por lo que debe recortar el conjunto de resultados ya sea temprano (como lo hice) o tarde (como lo hace Dave). – annakata

+0

Éste lo hace. Muchas gracias (a todos también)! –

1

No estoy 100% claro en lo que estás pidiendo, pero si leo este derecho tal vez sólo quiere:

/container/container/ancestor-or-self::container[1]/data[@id='1'] 

nota del "[1]"

Edición: pensado en ello un poco más y el siguiente funciona para mí en todos los casos en lo que va buscando @ id = $ N

/container/container/ancestor-or-self::container[data[@id=$N]][1]/data[@id=$N] 

xpathing Bastante extrema derecha allí. Básicamente lo que está sucediendo es que el conjunto de nodos del ancestro o el yo devuelve el orden más bajo en la posición 1, ese es el que desea, pero necesita poner una segunda condición para que ese nodo también tenga un nodo de datos que le interese, una vez que cumples esas condiciones, tienes el nodo correcto, ahora solo quieres avanzar más hacia los datos reales.

99% del tiempo de XPath se hace más fácil si se toma aparte y pensar en él como un algoritmo :)

+0

Lo intenté, pero eso no cubre todos los casos. ¿Qué sucede si en su lugar estoy buscando @ id = '2'? Quiero favorecer los resultados del nodo actual antes que los antepasados, pero ser capaz de retroceder. –

+0

Ver edición: creo que cubre todos los casos enumerados – annakata

0

En lugar de

@id="1" 

has:

position() == 1 
+0

Si la posición() == 1 funcionaría, simplemente soltando el antecesor-o-uno :: todos juntos. Creo que el requisito no es solo para cualquier primer nodo de datos sino también para un nodo de datos con una identificación de 1. La pregunta no es tan clara como podría ser. – AnthonyWJones

11

Asegúrese de que el last() está en el ámbito correctamente. Proveedores:

(/container/container/ancestor-or-self::container/data[@id="1"])[last()] 

mismo modo:

//container[last()] 

y:

(//container)[last()] 

no son la misma cosa. El primero devolverá el último nodo de contenedor en cada nivel de la jerarquía - múltiples coincidencias - y el segundo devolverá el último nodo de contenedor encontrado - una coincidencia.

+0

¡Ajá, esto funcionó! Gracias. –

+0

En realidad, descubrí que esto no funciona en el caso de que el nodo de contenedor hijo se especifique antes de un elemento de datos coincidente. –

+0

@Nick Spacek: mejoré mi respuesta a continuación, y ahora cubre incluso el caso de la esquina –

0

tratar [position() = 1]

ex.

/recipiente/contenedor/ancestro-o-self :: contenedor/datos [position() = 1]

Cuestiones relacionadas