Запрос XPath с предикатами text потомков и потомков - PullRequest
15 голосов
/ 13 октября 2010

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

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

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

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

Ответы [ 3 ]

39 голосов
/ 13 октября 2010

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

//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, должен получить тот же результат.

1 голос
/ 13 октября 2010
//*[self::div|self::table] 
   [descendant::text()[contains(.,"abc")]]  
   [not(descendant::div|descendant::table)]

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

1 голос
/ 13 октября 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)
]

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

...