2011-04-14 12 views
27

En la muestra xml anterior, me gustaría seleccionar todos los libros que pertenecen a la clase foo y no en la barra de clases mediante el uso de xpath.¿Cómo puedo seleccionar un elemento con múltiples clases con Xpath?

<?xml version="1.0" encoding="ISO-8859-1"?> 
<bookstore> 
    <book class="foo"> 
    <title lang="en">Harry Potter</title> 
    <author>J K. Rowling</author> 
    <year>2005</year> 
    <price>29.99</price> 
    </book> 
    <book class="foo bar"> 
    <title lang="en">Harry Potter</title> 
    <author>J K. Rowling</author> 
    <year>2005</year> 
    <price>29.99</price> 
    </book> 
    <book class="foo bar"> 
    <title lang="en">Harry Potter</title> 
    <author>J K. Rowling</author> 
    <year>2005</year> 
    <price>29.99</price> 
    </book> 
</bookstore> 
+2

Buena pregunta, 1. Vea mi respuesta para dos soluciones XPath 2.0 diferentes, de las cuales la primera podría ser la más eficiente de todas, especialmente con un motor XPath 2.0 que no optimiza. –

Respuesta

33

rellenando el valor @class con espacios iniciales y finales, se puede detectar la presencia de "foo" y "bar" y no preocuparse de si fue primero, medio, o el último, y cualquier falso positivo accesos a "alimento" o "estériles" @class valores:

/bookstore/book[contains(concat(' ',@class,' '),' foo ') 
     and not(contains(concat(' ',@class,' '),' bar '))] 
+1

¿Qué pasa si '@ class' contiene tabulador o incluso carácter de línea nueva en lugar de espacio. Aquí viene a mano la función 'normalize-space' (XPath 1.0) que elimina el espacio en blanco inicial y final de una cadena, reemplaza las secuencias de caracteres en espacios en blanco por un espacio simple, p. 'concat ('', normalize-space (@class), '')' –

+0

@Steven Pribilinskiy - Eso no debería ser necesario. Debido a la forma en que el analizador XML normaliza los valores de los atributos, las pestañas y los retornos de carro ya se habrán normalizado en un espacio. http://www.w3.org/TR/xml/#AVNormalize –

+0

Esta es una respuesta mejor: http://stackoverflow.com/a/3881148/557406 –

11

aunque me gusta solución Mads: Aquí es otro enfoque para XPath 2.0:

/bookstore/book[ 
       tokenize(@class," ")="foo" 
       and not(tokenize(@class," ")="bar") 
       ] 

Tenga en cuenta que t as siguientes expresiones son verdaderas:

("foo","bar")="foo" -> true 
("foo","bar")="bar" -> true 
+0

+1 para la solución XPath 2.0. Tantas cosas son más fáciles con 2.0. –

4

XPath 2.0:

/*/*[for $s in concat(' ',@class,' ') 
      return 
       matches($s, ' foo ') 
      and 
       not(matches($s, ' bar ')) 
     ] 

Aquí hay tokenización se hace y $ s se calcula sólo una vez.

O incluso:

/*/book[@class 
      [every $t in tokenize(.,' ') satisfies $t ne 'bar'] 
      [some $t in tokenize(.,' ') satisfies $t eq 'foo'] 
     ] 
+0

+1 para la optimización de cálculo único. – topless

+0

@ Chris-Top: De nada. –

Cuestiones relacionadas