web-dev-qa-db-ja.com

XPath:別の兄弟まで次の兄弟をすべて選択します

これが私のxmlの抜粋です。

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

_node[@id='1']_に配置されています。次の空でないノード(ここでは_<node/>_)まですべての_node[@id='2']_要素と一致するXpathが必要です。


編集: @id属性は、問題をより明確に説明するためのものであり、元のXMLにはありません。 @id属性を使用しないソリューションが必要です。


not _node[@id='2']_の後の空の兄弟と一致させたいので、単純なfollowing-sibling::node[text()='']を使用できません。

どうすればこれを達成できますか?

36
glmxndr

could次のようにします:

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

ここで、_'1'_は現在のノードのIDです(式を動的に生成します)。

式は言う:

  • 現在のコンテキストから親に移動します
  • 子ノードを選択します
  • テキストがない
  • すべての「IDを持つ先行する兄弟ノード」から、最初のノードは1のIDを持つ必要があります

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']))" />
_
21
Tomalak

受け入れられている答えよりも簡単です:

//node[@id='1']/following-sibling::node[following::node[@id='2']]
  • IDが「1」であるノードを見つけます
  • 次の兄弟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"/>
9
Phrogz