2010-08-16 16 views
6

tengo documentos XML como:XPath 'o' comportarse como unión ('|') con xmllib2

<rootelement> 
<myelement>test1</myelement> 
<myelement>test2</myelement> 
<myelement type='specific'>test3</myelement> 
</rootelement> 

me gustaría recuperar la específica myelement, y si no está presente, entonces el primero. Así que escribo:

/rootelement/myelement[@type='specific' or position()=1] 

Los XPath spec estados acerca de la 'o la expresión' que:

El operando de la derecha no se evalúa si el operando de la izquierda se evalúa como verdadera

El problema es que libxml2-2.6.26 parece aplicar la unión de ambas expresiones, devolviendo un "2 Node Set" (por ejemplo usando xmllint --shell).

¿Es libxml2 o estoy haciendo algo mal?

+0

Buena pregunta (1). Vea mi respuesta para una única expresión XPath que selecciona exactamente los nodos que desea. :) –

Respuesta

10

Respuesta breve: su selector no expresa lo que usted piensa que hace.


El operador or es una unión.

La parte de la especificación que ha citado ("El operando correcto no se evalúa ...") es parte de la norma boolean logic short circuiting.

He aquí por qué se obtiene un conjunto de 2 nodos por su ejemplo de entrada: XPath se ve en cada myelement que es un hijo de rootelement, y se aplica la parte [@type='specific' or position()=1] a cada uno de dichos nodos para determinar si es o no coincide con el selector.

  1. <myelement>test1</myelement> no coincide con @type='specific', pero coincide position()=1, para que coincida con todo el selector.
  2. <myelement>test2</myelement> no coincide con @type='specific', y tampoco coincide con position()=1, por lo que no coincide con el selector completo.
  3. <myelement type='specific'>test3</myelement> coincide con @type='specific' (por lo tanto, XPath no tiene que probar su posición, esa es la parte de cortocircuito) por lo que coincide con todo el selector.

La primera y la última <myelement> s coinciden con el selector completo, por lo que devuelve un conjunto de 2 nodos.

La manera más fácil de seleccionar elementos de la manera que desee es hacerlo en dos pasos.Aquí está el pseudocódigo (no sé qué contexto en realidad estás usando XPath en, y no estoy tan familiarizado con la escritura selectores XPath de sintaxis):

  1. Seleccionar elements que coincidan /rootelement/myelement[@type='specific']
  2. Si elements está vacío, seleccione elements que coincida con /rootelement/myelement[position()=1]
+0

te comerá: +1 para una muy buena explicación! –

7

@Matt Ball explicó muy bien la causa de su problema.

Aquí es un XPath selección de una sola línea exactamente lo que quiere:

/*/myelement[@type='specific'] | /*[not(myelement[@type='specific'])]/myelement[1] 
+0

Dimitre, parece que falta un cierre ')' en tu expresión. Su expresión selecciona exactamente el mismo nodo que el mío, es decir, test1 y test3, que no es mi intención. Por cierto, también probé (/ rootelement/myelement [@ type = 'specific' o position() = 1]) [1], lo que me da test1 ... no mejor. – foudfou

+0

@foudfou: gracias por notar esto. Lo arreglé ahora, así que inténtalo una vez más. :) –

Cuestiones relacionadas