Подтвердить что ты не робот

XPath: выберите всех следующих братьев и сестер, пока другой брат

Вот фрагмент моего xml:

<node/>
<node/>
<node id="1">content</node>
<node/>
<node/>
<node/>
<node id="2">content</node>
<node/>
<node/>

Я располагаюсь в node[@id='1']. Мне нужен Xpath, чтобы соответствовать всем элементам <node/>, пока следующий не пустой node (здесь node[@id='2']).


Edit: атрибуты @id должны только более четко объяснить мою проблему, но не в моем оригинальном XML. Мне нужно решение, которое не использует атрибуты @id.


Я делаю не, чтобы соответствовать пустым братьям и сестрам после node[@id='2'], поэтому я не могу использовать наивный following-sibling::node[text()=''].

Как я могу это достичь?

4b9b3361

Ответ 1

Вы можете сделать это следующим образом:

../node[not(text()) and preceding-sibling::node[@id][1][@id='1']]

где '1' - идентификатор текущего node (динамически генерирует выражение).

Выражение говорит:

  • из текущего контекста перейдите к родительскому
  • выберите те дочерние узлы, которые
  • нет текста и
  • из всех "предшествующих узлов-братьев, имеющих идентификатор", первый должен иметь идентификатор 1

Если вы находитесь в XSLT, вы можете выбрать одну из следующих осей, поскольку вы можете использовать функцию current():

<!-- the for-each is merely to switch the current node -->
<xsl:for-each select="node[@id='1']">
  <xsl:copy-of select="
    following-sibling::node[
      not(text()) and
      generate-id(preceding-sibling::node[@id][1])
      =
      generate-id(current())
    ]
  " />
</xsl:for-each>

или проще (и более эффективно) с помощью ключа:

<xsl:key 
  name="kNode" 
  match="node[not(text())]" 
  use="generate-id(preceding-sibling::node[@id][1])"
/>

<xsl:copy-of select="key('kNode', generate-id(node[@id='1']))" />

Ответ 2

XPath 2.0 имеет операторы '< < и ' → ', где node1 << node2 истинно, если node1 предшествует узлу2 в порядке документа. Таким образом, на основе XPath 2.0 в таблице стилей XSLT 2.0, где текущий node является node [@id = '1'], вы можете использовать

  following-sibling::node[not(text()) and . << current()/following-sibling::node[@od][1]]

Для этой функции также нужна функция current() из XSLT, поэтому я сказал "с XPath 2.0 в таблице стилей XSLT 2.0". Синтаксис, приведенный выше, является чистым XPath, в таблице стилей XSLT вам нужно будет вывести '< < как "& lt;".

Ответ 3

Проще, чем принятый ответ:

//node[@id='1']/following-sibling::node[following::node[@id='2']]
  • Найти node в любом месте, чей идентификатор "1"
  • Теперь найдите все следующие элементы sibling node
  • ... но только если эти элементы также имеют node с id="2" где-то после них.

Показан в действии с более четким тестовым документом (и юридическими id значениями):

xml = '<root>
<node id="a"/><node id="b"/>
<node id="c">content</node>
<node id="d"/><node id="e"/><node id="f"/>
<node id="g">content</node>
<node id="h"/><node id="i"/>
</root>'

# A Ruby library that uses libxml2; http://nokogiri.org
require 'nokogiri'; doc = Nokogiri::XML(xml)

expression = "//node[@id='c']/following-sibling::node[following::node[@id='g']]"
puts doc.xpath(expression)
#=> <node id="d"/>
#=> <node id="e"/>
#=> <node id="f"/>