Pytanie Zapytanie XPath z predykatami potomnymi i potomnymi text ()


Chciałbym utworzyć zapytanie XPath, które zwróci element "div" lub "table", o ile ma potomek zawierający tekst "abc". Jedynym zastrzeżeniem jest to, że nie może mieć żadnych potomków div ani stołu.

<div>
  <table>
    <form>
      <div>
        <span>
          <p>abcdefg</p>
        </span>
      </div>
      <table>
        <span>
          <p>123456</p>
        </span>
      </table>
    </form>
  </table>
</div>

Tak więc jedynym poprawnym wynikiem tego zapytania będzie:

/div/table/form/div 

Moja najlepsza próba wygląda mniej więcej tak:

//div[contains(//text(), "abc") and not(descendant::div or descendant::table)] | //table[contains(//text(), "abc") and not(descendant::div or descendant::table)]

ale nie zwraca poprawnego wyniku.

Dzięki za pomoc.


15
2017-10-13 05:15


pochodzenie


Dobre pytanie, +1. Zobacz moją odpowiedź na prawdopodobnie najkrótsze rozwiązanie. :) - Dimitre Novatchev


Odpowiedzi:


Coś innego: :)

//text()[contains(.,'abc')]/ancestor::*[self::div or self::table][1]

Wydaje się o wiele krótszy niż inne rozwiązania, prawda? :)

Tłumaczenie na prosty angielski: Dla dowolnego węzła tekstowego w dokumencie zawierającym ciąg "abc" wybierz jego pierwszego przodka, który jest albo div lub table.

To jest bardziej wydajne, ponieważ wymagane jest tylko jedno pełne skanowanie drzewa dokumentu (i żadne inne) oraz ancestor::* przejście jest bardzo tanie w porównaniu do a descendent:: (drzewo) skanowanie.

Aby sprawdzić, czy to rozwiązanie "naprawdę działa":

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

 <xsl:template match="/">
  <xsl:copy-of select=
  "//text()[contains(.,'abc')]/ancestor::*[self::div or self::table][1] "/>
 </xsl:template>
</xsl:stylesheet>

kiedy ta transformacja jest wykonywana na dostarczonym dokumencie XML:

<div>
  <table>
    <form>
      <div>
        <span>
          <p>abcdefg</p>
        </span>
      </div>
      <table>
        <span>
          <p>123456</p>
        </span>
      </table>
    </form>
  </table>
</div>

pożądany, prawidłowy wynik jest produkowany:

<div>
   <span>
      <p>abcdefg</p>
   </span>
</div>

Uwaga: Nie jest konieczne korzystanie z XSLT - każdy host XPath 1.0 - taki jak DOM - musi uzyskać taki sam wynik.


32
2017-10-13 12:57



dziękuję za odpowiedź i dziękuję za +1. Wolę zwięzłość tej odpowiedzi, ale nie mogę jej uruchomić w moich testach. Pozostałe dwie odpowiedzi na to pytanie działają dla mnie. Czy to możliwe, że w twojej odpowiedzi jest literówka? Nie mogę twierdzić, że wszystko rozumiem. Co robi [1]? Ponownie, jeśli masz jakiś wgląd w to, dlaczego ta odpowiedź nie działa dla mnie, a inni to robią, byłbym wdzięczny. Chciałbym dać +1 na Twój czas, ale jestem nowy na tej stronie i nie mam jeszcze takiej możliwości. Dzięki. - juan234
@ juan234: Dodałem do mojej odpowiedzi kod weryfikacyjny, który każdy może uruchomić i zweryfikować poprawność wyniku. Ta weryfikacja pokazuje poprawność wyrażenia - jest Nie literówka. Mogą pojawić się problemy z różnych powodów: od wykorzystania niekompatybilnego silnika XPath 1.0 do problemów w kodzie - aby wskazać powód, dla którego konieczne jest zobaczenie kodu. [1] oznacza pierwszy węzeł zestawu węzłów wybrany przez część wyrażenia znajdującą się bezpośrednio po prawej stronie [1] - w osiach odwrotnych (np ancestor:: to faktycznie oznacza ostatni węzeł w porządku dokumentu). - Dimitre Novatchev
Jestem przekonany :) - juan234
Wiem, że to jest stare ... ale natknąłem się na to, szukając różnych sposobów na dopasowywanie tekstu u potomków ... to jest eleganckie i łatwe do zrozumienia po zobaczeniu ... ale na tyle sprytne, że miałem zobaczyć to pierwszy, a teraz wiem trochę więcej o xpath :) - Dan Nguyen
@DanNguyen, tak, XPath jest fascynującym językiem. Jeśli jesteś zainteresowany tymi tematami, bezwstydnie poleciłbym moje kursy: "XSLT 2.0 i 1.0 Foundations" (pluralsight.com/courses/xslt-foundations-part1) - obejmuje XPath 1.0 i XPath 2.0 oraz kurs "Ewolucja XPath: Co nowego w XPath 3.0" (pluralsight.com/courses/xpath-3-0-whats-new) - obejmuje XPath 3.0 - Dimitre Novatchev


możesz spróbować:

//div[
  descendant::text()[contains(., "abc")] 
  and not(descendant::div or descendant::table)
] | 
//table[
  descendant::text()[contains(., "abc")] 
  and not(descendant::div or descendant::table)
]

To pomaga?


1
2017-10-13 09:48





//*[self::div|self::table] 
   [descendant::text()[contains(.,"abc")]]  
   [not(descendant::div|descendant::table)]

Problem z contains(//text(), "abc") jest to, że funkcje obsadzają węzły ustawiając pierwszy węzeł.


1
2017-10-13 12:30