2009-04-06 14 views
89

Biorąc pod uwagę to uproszczony format danych:XPath, aby wybrać wiele tagów

<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> 

Jak można wybrać wszystkie C s, D s i E s, które są dzieci B elementów?

Zasadniczo, coś jak:

a/b/(c|d|e) 

we własnej sytuacji, a nie tylko a/b/, zapytanie prowadzące do wybierania tych C, D, E węzłów jest rzeczywiście dość skomplikowane, więc chciałbym, aby uniknąć w ten sposób:

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

Czy to możliwe?

Odpowiedz

150

One Poprawną odpowiedzią jest:

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

Należy pamiętać t kapelusz to

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

jest zarówno zbyt długo i niewłaściwe. To wyrażenie XPath wybiorą węzły takie jak:

OhMy:c 

NotWanted:d 

QuiteDifferent:e 
+0

"lub" nie działa na urządzenie, należy zamiast tego użyć linii pionowej "|" –

+5

@ Guasqueño, 'or' jest operatorem logicznym - działa na dwóch wartościach logicznych. Operator unii XPath ** union | | 'działa na dwóch zestawach węzłów. Są one zupełnie inne i istnieją konkretne przypadki użycia dla każdego z nich. Użycie '' '** can ** rozwiązuje pierwotny problem, ale powoduje dłuższe i bardziej złożone i trudne do zrozumienia wyrażenie XPath. Prostsze wyrażenie w tej odpowiedzi, które używa operatora 'lub ', tworzy pożądany zestaw węzłów i * może * być określone w atrybucie" select "operacji' 'XSLT. Po prostu spróbuj. –

40

Można uniknąć powtórki z testem atrybutu Zamiast:

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

Wbrew antagonistycznym opinii Dimitre, tym wyżej jest nie nieprawidłowy w próżni, gdzie PO nie ma określonej interakcji z nazw. Oś self:: jest restrykcyjna w zakresie nazw, local-name() nie jest. Jeśli intencją PO jest przechwytywanie c|d|e niezależnie od przestrzeni nazw (co sugerowałbym, że jest to nawet prawdopodobny scenariusz, biorąc pod uwagę OR problemu), to jest to "inna odpowiedź, która wciąż ma pozytywne głosy", która jest niepoprawna.

Nie możesz być definitywny bez definicji, chociaż jestem szczęśliwy, że mogę skasować moją odpowiedź jako rzeczywiście niepoprawną, jeśli PO wyjaśni swoje pytanie tak, że jestem niepoprawny.

+0

My * bardzo wyraźnie wykonane * Chodzi o to, że nie może ani ty, ani ja nie być ostateczne, i że downvote jest niesłuszna, ponieważ moja odpowiedź nie jest wręcz źle. Fakt, że OP akceptuje odpowiedź, jest nieistotny, ponieważ nie może zaakceptować obu, oba będą działać w większości przypadków, a mój został usunięty w tym czasie. – annakata

+3

Co więcej, mówię, że podważam twoje tak zwane "fakty", i to jest * moja * opinia, że ​​jawnie antagonizmujesz. W jakim celu twoje komentarze "wciąż mają pozytywne głosy" i "wyjaśniam, dlaczego ten, który jest obecnie najbardziej głosowany, jest nieprawidłowy" służysz? – annakata

+0

Nie, nie, nie da się dokładnie określić - wielokrotnie powtarzałem: oba mogą być poprawne, oba działają na prostym dokumencie, zależy to całkowicie od tego, co OP chce od umieszczenia w przestrzeni nazw. Wydaje się, że świadomie o tym nie wiesz, być może dlatego, że może cię to zepsuć? – annakata

-2

Nie wiem, czy to pomoże, ale z XSL, zrobiłbym coś takiego:

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

i nie będzie to XPath zaznaczyć wszystkie dzieci węzłów B:

a/b/* 
+0

Dzięki Calvin, ale nie używam XSL, a pod B nie ma więcej elementów, których nie chcę wybierać. Zaktualizuję mój przykład, aby był jaśniejszy. – nickf

+0

No cóż, w tym przypadku wydaje się, że annakata ma rozwiązanie. – Calvin

11

dlaczego nie a/b/(c|d|e)? Po prostu próbowałem z Saxon XML library (ładnie zapakowane z odrobiną Clojure) i wydaje się, że działa. abc.xml to dokument opisany przez 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

Tak, ale to jest XPath 2.0 –

+1

@Alejandro, spoko! Nie było mnie (z błogością) ze świata XML od 4 lat, wygląda na to, że XPath przesunął się do 2.0 :) –

+0

To działało dobrze dla mnie. Wygląda na to, że XPath 2.0 jest domyślny dla parsowania HTML w lxml na Pythonie 2. –

Powiązane problemy