Запрос XPath с потомком и потомком text() предикатов

Я хотел бы построить запрос XPath, который будет возвращать элемент «div» или «table», если у него есть потомок, содержащий текст «abc». Одно предостережение заключается в том, что у него не может быть потомков div или table.

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

Таким образом, единственным правильным результатом этого запроса будет:

/div/table/form/div 

Моя лучшая попытка выглядит примерно так:

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

но не возвращает правильный результат.

Спасибо за вашу помощь.


person juan234    schedule 13.10.2010    source источник


Ответы (3)


Что-то другое: :)

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

Кажется намного короче, чем другие решения, не так ли? :)

Переведено на простой английский: для любого текстового узла в документе, содержащего строку "abc", выберите его первого предка, который является либо div, либо table.

Это более эффективно, так как требуется только одно полное сканирование дерева документа (а не какое-либо другое), а ancestor::* обход обходится очень дешево по сравнению с descendent:: (дерево) сканированием.

Чтобы убедиться, что это решение "действительно работает":

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

когда это преобразование выполняется с предоставленным XML-документом:

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

получен желаемый правильный результат:

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

Примечание. Нет необходимости использовать XSLT — любой хост XPath 1.0, такой как DOM, должен получить такой же результат.

person Dimitre Novatchev    schedule 13.10.2010
comment
спасибо за ваш ответ и спасибо за +1. Я предпочитаю компактность этого ответа, однако я не могу заставить его работать в моих тестах. Два других ответа на этот вопрос работают для меня. Возможно ли, что в вашем ответе опечатка? Я не могу утверждать, что понимаю все это. Что делает [1]? Опять же, если у вас есть понимание, почему этот ответ не работает для меня, а другие работают, я был бы признателен. Я бы +1 за ваше время, но я новичок на этом сайте и пока не имею возможности. Спасибо. - person juan234; 14.10.2010
comment
@ juan234: я добавил к своему ответу некоторый проверочный код, который каждый может запустить и проверить правильность результата. Эта проверка показывает правильность выражения - никакой опечатки. У вас могут возникнуть проблемы по разным причинам: от использования несовместимого движка XPath 1.0 до проблем в вашем коде — чтобы точно определить причину, необходимо увидеть ваш код. [1] означает первый узел набора узлов, выбранный частью выражения, которая находится непосредственно справа от [1] -- в обратных осях (например, ancestor:: фактически означает последний узел в порядке документа). - person Dimitre Novatchev; 14.10.2010
comment
Я знаю, что это старо... но я просто наткнулся на это, ища разные способы сопоставления текста внутри потомков... это элегантно и легко понять, увидев это... но достаточно умно, чтобы я чтобы увидеть это первым, и теперь я знаю немного больше о xpath :) - person dancow; 19.06.2015
comment
@DanNguyen, да, XPath - увлекательный язык. Если вас интересуют эти темы, я бы без зазрения совести порекомендовал свои курсы: XSLT 2.0 и 1.0 Foundations (pluralsight.com/courses/xslt-foundations-part1) — охватывает XPath 1.0 и XPath 2.0, а также курс Эволюция XPath: что нового в XPath 3.0 (pluralsight.com/courses/xpath-3-0-whats-new) — охватывает XPath 3.0. - person Dimitre Novatchev; 19.06.2015

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

Проблема с contains(//text(), "abc") заключается в том, что функции приведения наборов узлов берут первый узел.

person Community    schedule 13.10.2010

вы можете попробовать:

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

это помогает?

person Dennis Münkle    schedule 13.10.2010