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()='']
.
Как я могу это достичь?
Ответы
Ответ 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"/>