XPath: выберите всех следующих братьев до следующей - PullRequest
36 голосов
/ 29 января 2010

Вот выдержка из моего xml:

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

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


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


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

Как мне этого добиться?

Ответы [ 3 ]

21 голосов
/ 29 января 2010

Вы могли бы сделать это так:

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

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

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

  • из текущего контекста перейти к родителю
  • выберите те дочерние узлы, которые
  • не имеет текста и
  • из всех «предыдущих узлов-братьев, имеющих идентификатор», первый должен иметь идентификатор 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']))" />
9 голосов
/ 20 октября 2011

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

//node[@id='1']/following-sibling::node[following::node[@id='2']]
  • Найти узел в любом месте с идентификатором '1'
  • Теперь найдите всех следующих братьев и сестер node elements
  • ... но только если эти элементы также имеют 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"/>
8 голосов
/ 29 января 2010

XPath 2.0 имеет операторы «<<» и «>>», где node1 << node2 равно true, если узел1 предшествует узлу2 в порядке документа. Таким образом, основываясь на XPath 2.0 в таблице стилей XSLT 2.0, где текущим узлом является узел [@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; & lt; '. </p>

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