Как бы вы нашли все узлы между двумя H3, используя XPATH? - PullRequest
22 голосов
/ 01 октября 2010

Как бы вы нашли все узлы между двумя H3, используя XPATH?

Ответы [ 5 ]

27 голосов
/ 01 октября 2010

В XPath 1.0 один из способов сделать это - использовать метод Кейсиана для пересечения множества узлов :

$ns1[count(.|$ns2) = count($ns2)]

Вышеприведенное выражение выбирает именно те узлы, которые являются частью набора узлов $ns1 и набора узлов $ns2.

Чтобы применить это к конкретному вопросу - скажем, нам нужно выбрать все узлы между вторым и третьим h3 элементом в следующем XML-документе:

<html>
  <h3>Title T31</h3>
    <a31/>
    <b31/>
  <h3>Title T32</h3>
    <a32/>
    <b32/>
  <h3>Title T33</h3>
    <a33/>
    <b33/>
  <h3>Title T34</h3>
    <a34/>
    <b34/>
  <h3>Title T35</h3>
</html>

Мы должны заменить $ns1 на :

/*/h3[2]/following-sibling::node()

и заменить $ns2 на :

/*/h3[3]/preceding-sibling::node()

Таким образом, полное выражение XPath равно :

/*/h3[2]/following-sibling::node()
             [count(.|/*/h3[3]/preceding-sibling::node())
             =
              count(/*/h3[3]/preceding-sibling::node())
             ]

Мы можем убедиться, что это правильное выражение XPath:

<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=
   "/*/h3[2]/following-sibling::node()
             [count(.|/*/h3[3]/preceding-sibling::node())
             =
              count(/*/h3[3]/preceding-sibling::node())
             ]
   "/>
 </xsl:template>
</xsl:stylesheet>

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

<a32/>

<b32/>

II. Решение XPath 2.0 :

Используйте оператор intersect :

   /*/h3[2]/following-sibling::node()
intersect
   /*/h3[3]/preceding-sibling::node()
7 голосов
/ 01 октября 2010

Другое решение XPath 1.0, когда вы знаете, что обе метки являются одним и тем же элементом (в данном случае h3):

/html/body/h3[2]/following-sibling::node()
                           [not(self::h3)]
                           [count(preceding-sibling::h3)=2]
2 голосов
/ 01 октября 2010

Более общее решение - в XPath 2.0 - при условии, что вам нужны узлы на всех глубинах дерева между двумя элементами h3, которые не обязательно должны быть родными.

/path/to/first/h3/following::node()[. << /path/to/second/h3]
1 голос
/ 02 октября 2010

Основываясь на dimitre-novatchev отличный ответ, я могу предложить следующее решение: вместо жесткого кодирования [2] и [3] для разных H3 я просто даю содержимое заголовка первого элемента ,

//h3[text()="Main Page Section Heading"]/following-sibling::node()
 [  count(.|//h3[text()="Main Page Section Heading"]/following-sibling::h3[1]/preceding-sibling::node()) =  
    count(//h3[text()="Main Page Section Heading"]/following-sibling::h3[1]/preceding-sibling::node())  ]

Хотя я бы хотел пойти дальше, чтобы иметь возможность разобраться со сценарием, когда я смотрю на последний H3, и получить все после него, в приведенном выше случае я не могу получить то, что следует за последним H3.

0 голосов
/ 20 декабря 2013

Существует еще одно отличное универсальное решение, использующее ключи, при условии, что у ваших тегов <h3> есть уникальное свойство (например, его текст или атрибут id):

<xsl:key name="siblings_of_h3" match="*[not(self::h3)]" use="preceding-sibling::h3[1]/text()"/>

<xsl:template match="h3">
  <!-- now select all tags belonging to the current h3 -->
  <xsl:apply-templates select="key('siblings_of_h3', text())"/>
</xsl:template>

Он группирует все теги по их предшествующим<h3>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...