2011-04-14 15 views
27

W powyższym przykładzie xml chciałbym wybrać wszystkie książki należące do klasy foo, a nie na pasku klasy, używając xpath.Jak mogę wybrać element z wieloma klasami z 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

Dobre pytanie, +1. Zobacz moją odpowiedź na dwa różne rozwiązania XPath 2.0, z których pierwszy może być najbardziej wydajny z nich wszystkich, zwłaszcza z nieoptymalizującym silnikiem XPath 2.0. –

Odpowiedz

33

przez napawanie wartość @class z początkowe i końcowe spacje, można przetestować pod kątem obecności „foo” i „bar” i nie martwić się o to, czy to był pierwszy, środkowy, albo wreszcie, i każdy fałszywy alarm hity na "żywność" lub "jałowe" @class wartości:

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

Co jeśli '@ class' zawiera znak tabulacji, a nawet znak nowej linii zamiast spacji. Oto przydatna funkcja 'normalize-space' (XPath 1.0), która usuwa początkową i końcową białą przestrzeń z ciągu znaków, zastępując sekwencje białych znaków pojedynczą spacją, np. 'concat ('', normalize-space (@class), '')' –

+0

@Steven Pribilinskiy - To nie powinno być konieczne. Ze względu na to, jak wartości atrybutów są normalizowane przez analizator składni XML, tabulatory i deklaracje karetki zostały już znormalizowane w spację. http://www.w3.org/TR/xml/#AVNormalize –

+0

To jest lepsza odpowiedź: http://stackoverflow.com/a/3881148/557406 –

11

Chociaż lubię rozwiązanie Mads: Oto kolejny podejście do XPath 2.0:

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

Należy pamiętać, że T on następujące wyrażenia są zarówno prawdziwe:

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

+1 dla rozwiązania XPath 2.0. Tak wiele rzeczy jest łatwiejszych w wersji 2.0. –

4

XPath 2.0:

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

Tu nie tokenizacja jest zrobione i $ s oblicza się tylko raz.

Albo nawet:

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

+1 dla pojedynczej optymalizacji obliczeń. – topless

+0

@ Chris-Top: Nie ma za co. –

Powiązane problemy