2011-04-14 19 views
27

Nel precedente esempio xml vorrei selezionare tutti i libri che appartengono alla classe pippo e non nella barra della classe usando xpath.Come posso selezionare un elemento con più classi 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

Buona domanda, +1. Vedere la mia risposta per due diverse soluzioni XPath 2.0 di cui la prima potrebbe essere la più efficiente di tutte, specialmente con un motore XPath 2.0 non ottimizzante. –

risposta

33

Con imbottitura il valore @class con spazi iniziali e finali, è possibile verificare la presenza di "foo" e "bar" e non preoccuparsi se era prima, centrale, o l'ultimo, e qualsiasi falsi positivi visite per "cibo" o "sterili" @class valori:

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

Cosa succede se '@ class' contiene tab o anche carattere di nuova riga invece di spazio. Ecco a portata di mano la funzione 'normalize-space' (XPath 1.0) che rimuove lo spazio bianco iniziale e finale da una stringa, sostituisce le sequenze di caratteri di spazi bianchi con un singolo spazio, ad es. 'concat ('', normalize-space (@class), '')' –

+0

@Steven Pribilinskiy - Non dovrebbe essere necessario. A causa del modo in cui i valori degli attributi vengono normalizzati dal parser XML, le tabulazioni e i ritorni a capo saranno già stati normalizzati in uno spazio. http://www.w3.org/TR/xml/#AVNormalize –

+0

Questa è una risposta migliore: http://stackoverflow.com/a/3881148/557406 –

11

Anche se mi piace la soluzione di Mads: Ecco un altro approccio per XPath 2.0:

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

prega di notare che t egli seguenti espressioni sono entrambe vere:

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

+1 per la soluzione XPath 2.0. Così tante cose sono più facili con 2.0. –

4

XPath 2.0:

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

Qui nessuno tokenizzazione è fatto e $ s viene calcolato solo una volta.

O anche:

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

+1 per l'ottimizzazione del singolo calcolo. – topless

+0

@ Chris-Top: prego. –

Problemi correlati