如何保持Nokogiri的NodeSet.search方法的空白结果

时间:2010-12-19 10:22:05

标签: ruby search nokogiri

我想基于一个名为nodeset的NodeSet运行Nokogiri :: XML :: NodeSet的搜索方法,用于某些xpath规则,如下所示:

nodeset.search(rule)

上面的代码返回一个NodeSet,但不包含那些与规则不匹配的NodeSet。我的意图是:如果节点集中的元素与规则匹配,那么请返回匹配的结果;如果不匹配,请在结果中返回一个空字符串,以便我可以知道调用者节点集中的哪个元素匹配,调用者节点集中的哪个元素不匹配。

有人可以告诉我该怎么做吗?我非常感谢你的帮助。

3 个答案:

答案 0 :(得分:1)

Nokogiri NodeSet支持类似于Ruby数组的集合操作。不要在匹配的集合中保留空白,而是在事后找出错过的项目:

require 'nokogiri'

doc = Nokogiri::XML <<-ENDXML
<root>
  <a id="a1" class="foo">
    <a id="a1a" class="foo" />
    <a id="a1b" class="foo" andalso="this" />
  </a>
  <a id="a2" class="foo" andalso="this">
    <a id="a2a" class="bar" />
    <a id="a2b" class="bar" andalso="this" />
  </a>
  <a id="a3" class="foo" andalso="this" />
</root>
ENDXML

foos = doc.xpath('//a[@class="foo"]')
p foos.map{ |e| e['id'] }
#=> ["a1", "a1a", "a1b", "a2"]

subselect = foos.xpath('self::*[@andalso="this"]')
p subselect.map{ |e| e['id'] }
#=> ["a1b", "a2", "a3"]

missed = foos - subselect
p missed.map{ |e| e['id'] }
#=> ["a1", "a1a"]

如果您确实需要结果中的非节点,则必须使用#map代替#search或其他Nokogiri方法,并获得Array而不是{{1 }}:

NodeSet

答案 1 :(得分:0)

我不太了解nokogiri,知道这有多好,但我怀疑下面的例子可能会提出一个前进的方向。以下假设NodeSet的行为类似于根据其API文档[1]

执行的ruby数组
a = (0..9).to_a
 => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
evens = a.select { |i| i % 2 == 0 }
 => [0, 2, 4, 6, 8]
odds = a - evens
 => [1, 3, 5, 7, 9]

我相信您应该能够对您的节点集执行类似操作,以便在执行搜索时,您可以通过从原始节点集中减去新节点集来找到不匹配的节点。

[1] http://nokogiri.rubyforge.org/nokogiri/Nokogiri/XML/NodeSet.html#M000448

答案 2 :(得分:0)

以下是我如何去做的事情:

require 'nokogiri'

xml = <<EOT
<xml>
  <find_node>foo</find_node>
  <ignore_node>bar</ignore_node>
  <find_node>foo</find_node>
  <ignore_node>bar</ignore_node>
</xml>
EOT

# parse the document...
doc = Nokogiri::XML(xml)

# find the nodes we want...
desired_nodes = doc.search('//find_node')

# see if it's working...
desired_nodes.map{ |n| n.to_xml } # => ["<find_node>foo</find_node>", "<find_node>foo</find_node>"]

# walk the tree, grabbing the text or '' depending on whether the node is a hit or a miss...
node_result = doc.search('/xml/*').map{ |n| desired_nodes.include?(n) ? n.text : '' }

# ** here's the result **
node_result # => ["foo", "", "foo", ""]

# if we wanted to we could grab the desired_nodes' text...
desired_nodes.map{ |n| n.text } # => ["foo", "foo"]

# or find the ignored nodes...
ignored_nodes = doc.search('/xml/*') - desired_nodes
ignored_nodes.map{ |n| n.to_xml } # => ["<ignore_node>bar</ignore_node>", "<ignore_node>bar</ignore_node>"]

# ...and grab the ignored_nodes' text...
ignored_nodes.map{ |n| n.text } # => ["bar", "bar"]