2012-10-13 11 views
25

Biorąc markup tak:Czy istnieje lepszy sposób na uzyskanie węzła nadrzędnego wyniku zapytania XPath?

<div class="foo"> 
    <div><span class="a1"></span><a href="...">...</a></div> 
    <div><span class="a2"></span><a href="...">...</a></div> 
    <div><span class="a1"></span>some text</div> 
    <div><span class="a3"></span>some text</div> 
</div> 

Teraz jestem zainteresowany w uzyskaniu te <a> i some text TYLKO jeśli sąsiednim span jest klasy a1. Więc na końcu całego kodu mój wynik to <a> od pierwszego div i z trzeciego. Byłoby łatwo, gdyby <a> i i some text miałyby wartość span lub div miałyby atrybut class, ale bez powodzenia.

Co robię teraz jest kwerenda dla span z a1 klasa tak:

//div[contains(@class,'foo')]/div/span[contains(@class,'a1')] 

a następnie uzyskać jego rodzica i zrobić kolejny query() z tego węzła nadrzędnego jako węzła kontekstowego. To wygląda na dalekie od skuteczności, więc czy jest jakiś lepszy sposób na zrobienie tego?


ODPOWIEDŹ

Zgodnie @MarcB answer prawo zapytania to:

//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/.. 

ale <A> może lepiej byłoby użyć:

//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/../a 

uzyskać <A> w zamiast jego pojemnika.

Odpowiedz

54

Zaletą zapytań XPath jest to, że można w zasadzie traktować je jak ścieżki systemu plików, więc po prostu o

//div[contains(@class,'foo')]/div/span[contains(@class,'a1')]/.. 
                   ^^ 

znajdzie wszystkie węzły .a1 które są poniżej węzła .foo, następnie ruch o jeden poziom do rodziców a1 węzłów.

+4

+1 na odniesienie do pliku ścieżek systemowych, to jak mam zawsze o tym myślałem, ale nigdy nie słyszałem, żeby to zostało wyjaśnione. –

+1

Właśnie sprawdziłem instrukcję przed zadaniem pytania, ale wydaje mi się, że udało mi się ominąć "..", ponieważ wyraźnie tam jest. Jednak referencja FS wyjaśniła od razu. Dzięki. –

+1

Tak. kiedy pierwszy raz wskoczyłem na ścieżkę xpath, przez chwilę omijałem tę kwestię, ale ustalenie ścieżki zapytania było dla mnie momentem eureki. –

16

Wyrażenie to lepiej niż przy użyciu odwrotnej oś:

//div[contains(@class,'foo')]/div[span[contains(@class,'a1')]] 

to wybranie dowolnego div który jest dzieckiem atrybutu div którego class zawierała „foo” i (Wybrana div) ma dziecko, którego atrybut class zawiera ciąg "a1".

XSLT - na podstawie weryfikacji:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="/"> 
    <xsl:copy-of select= 
    "//div[contains(@class,'foo')] 
      /div[span[contains(@class,'a1')]]"/> 
</xsl:template> 
</xsl:stylesheet> 

Kiedy transformacja ta jest stosowana na dostarczonych dokumentów XML:

<div class="foo"> 
    <div><span class="a1"></span><a href="...">...</a></div> 
    <div><span class="a2"></span><a href="...">...</a></div> 
    <div><span class="a1"></span>some text</div> 
    <div><span class="a3"></span>some text</div> 
</div> 

wyrażenie XPath jest oceniane i wybrane elementy są kopiowane na wyjściu:

<div> 
    <span class="a1"/> 
    <a href="...">...</a> 
</div> 
<div> 
    <span class="a1"/>some text</div> 

II. Uwagi o dostępie do elementu HTML przez jednego ze swoich klas:

Jeśli wiadomo, że element może mieć tylko jedną klasę, to nie jest w ogóle konieczne do korzystania

Nie używaj:

//div[contains(@class, 'foo')] 

Zastosowanie:

//div[@class = 'foo'] 

lub, jeśli nie może być wiodącym/spacje, należy:

//div[normalize-space(@class) = 'foo'] 

Kluczowym problemem z:

//div[contains(@class, 'foo')] 

jest to, że wybiera dowolną div z klasy, takie jak "myfoo" , "foo2" lub "myfoo3".

Jeśli element może mieć więcej niż jedną klasę, i aby uniknąć powyższą kwestię, poprawne wyrażenie XPath jest:

//div[contains(concat(' ', @class, ' '), ' foo ')] 
+2

nie zapominaj, że html pozwala na wiele klas. '@ class = 'foo'' będzie pomijać' class = "foo bar baz" '. jako taki, @contains jest całkowicie poprawny, tak długo (jak zauważysz), oglądasz fałszywe alarmy –

+3

@MarcB, Wydaje się, że nie przeczytałeś lub nie zrozumiałeś tej odpowiedzi - Traktuje ona na długo przypadek, w którym element ma więcej niż jedną klasę. Co więcej, ta odpowiedź dostarcza poprawnego rozwiązania dla tej sprawy - nie tak jak niepoprawne i uproszczone 'zawiera (@calss, someString)' –

Powiązane problemy