2009-04-06 17 views
89

Teniendo en cuenta este formato de datos simplificada:XPath para seleccionar varias etiquetas

<a> 
    <b> 
     <c>C1</c> 
     <d>D1</d> 
     <e>E1</e> 
     <f>don't select this one</f> 
    </b> 
    <b> 
     <c>C2</c> 
     <d>D2</d> 
     <e>E1</e> 
     <g>don't select me</g> 
    </b> 
    <c>not this one</c> 
    <d>nor this one</d> 
    <e>definitely not this one</e> 
</a> 

¿Cómo seleccionar todos los C s, s D y E s que son hijos de B elementos?

Básicamente, algo así como:

a/b/(c|d|e) 

En mi propia situación, en lugar de sólo a/b/, la consulta que condujo a la selección de los C, D, E nodos es en realidad bastante compleja por lo que me gustaría evitar haciendo esto:

a/b/c|a/b/d|a/b/e 

¿Esto es posible?

Respuesta

150

Uno respuesta correcta es:

/a/b/*[self::c or self::d or self::e] 

hacer la nota t sombrero de este

a/b/*[local-name()='c' or local-name()='d' or local-name()='e'] 

es a la vez demasiado larga e incorrecta. Esta expresión XPath seleccionará los nodos como:

OhMy:c 

NotWanted:d 

QuiteDifferent:e 
+0

'o' no funciona en uno para cada uno, necesitaría usar una línea vertical en su lugar '|' –

+5

@ Guasqueño, 'o' es un operador lógico: opera en dos valores booleanos. El operador XPath ** union ** '|' opera en dos conjuntos de nodos. Estos son bastante diferentes y hay casos de uso específicos para cada uno de ellos. Usar '|' ** puede ** resolver el problema original, pero resulta en una expresión XPath más larga y compleja y desafiante. La expresión más simple en esta respuesta, que usa el operador 'o' produce el conjunto de nodos deseado y * puede * ser especificado en el atributo" seleccionar "de una operación XSLT' '. Solo inténtalo. –

40

Puede evitar la repetición con una prueba de atributo en su lugar:

a/b/*[local-name()='c' or local-name()='d' or local-name()='e'] 

Contrariamente a la opinión antagónica de Dimitre, lo anterior es no incorrectos en el vacío, donde el PO no ha especificado la interacción con los espacios de nombres. El eje self:: es restrictivo del espacio de nombres, local-name() no lo es. Si la intención del OP es capturar c|d|e independientemente del espacio de nombres (lo que sugeriría es incluso un escenario probable dada la naturaleza OR del problema), entonces es "otra respuesta que todavía tiene algunos votos positivos", lo que es incorrecto.

No puede ser definitivo sin definición, aunque estoy muy contento de eliminar mi respuesta como realmente incorrecta si el OP aclara su pregunta de manera que soy incorrecto.

+0

Mi punto * muy claro es que ni usted ni yo podemos ser definitivos, y que su voto negativo está fuera de lugar ya que mi respuesta no es totalmente errónea. El hecho de que OP acepte una respuesta es irrelevante, ya que no puede aceptar ambos, ambos funcionarán en la mayoría de los casos, y el mío fue eliminado en ese momento. – annakata

+3

Además, estoy diciendo que cuestiono sus llamados "hechos", y es * mi * opinión que usted se muestra claramente antagónico. ¿Qué propósito tienen sus comentarios "todavía tiene votos positivos" y "explico por qué el servicio más votado en este momento es incorrecto"? – annakata

+0

No, no, no se puede determinar con exactitud. Lo he dicho repetidamente: ambos pueden ser correctos, ambos son funcionales en un documento simple, depende completamente de lo que el OP desee del espacio de nombres. Pareces deliberadamente ignorante de esto, ¿tal vez porque podría ponerte en el error? – annakata

-2

No estoy seguro si esto ayuda, pero con XSL, me gustaría hacer algo como:

<xsl:for-each select="a/b"> 
    <xsl:value-of select="c"/> 
    <xsl:value-of select="d"/> 
    <xsl:value-of select="e"/> 
</xsl:for-each> 

y no esta XPath seleccionar todos los hijos de los nodos B:

a/b/* 
+0

Gracias Calvin, pero no estoy usando XSL, y en realidad hay más elementos debajo de B que no quiero seleccionar. Actualizaré mi ejemplo para ser más claro. – nickf

+0

Oh, bueno, en ese caso, Annakata parece tener la solución. – Calvin

11

Por qué no a/b/(c|d|e)? Acabo de probar con Saxon XML library (envuelto muy bien con un poco de Clojure bondad), y parece funcionar. abc.xml es el documento descrito por OP.

(require '[saxon :as xml]) 
(def abc-doc (xml/compile-xml (slurp "abc.xml"))) 
(xml/query "a/b/(c|d|e)" abc-doc) 
=> (#<XdmNode <c>C1</c>> 
    #<XdmNode <d>D1</d>> 
    #<XdmNode <e>E1</e>> 
    #<XdmNode <c>C2</c>> 
    #<XdmNode <d>D2</d>> 
    #<XdmNode <e>E1</e>>) 
+4

Sí, pero eso es XPath 2.0 –

+1

@Alejandro, genial! Estuve lejos (felizmente) del mundo XML durante 4 años, parece que XPath ha avanzado hasta 2.0 :) –

+0

Esto funcionó bien para mí. Parece que XPath 2.0 es el predeterminado para el análisis de HTML en lxml en Python 2. –

Cuestiones relacionadas