如何使用Nokogiri过滤数组(搜索,过滤,条件等)

时间:2014-12-01 01:58:34

标签: ruby-on-rails ruby nokogiri

我有一个XML文档,我需要用Nokogiri解析,但是我需要过滤掉所有'角色'名称与请求的节点不匹配的节点。

基本上我想返回一个只有那些角色的数组,其中第一个和最后一个名称与那些所需的匹配。

当前状态:

除了控制器内的一条过滤/搜索行外,我的所有代码都有效。我已经浏览了Nokogiri的过滤器搜索功能,但似乎无法达到预期的效果。

XML输入

<xml>
<role xsi:type="director"> 
 <firstName>Thomas</firstName> 
 <lastName>JONES</lastName>
 <company>Jones Enterprises</company>
</role>
<role xsi:type="director"> 
 <firstName>Thomas</firstName> 
 <lastName>TEST</lastName>
 <company>Test Factory</company>
</role>
</xml>

控制器

firstname = 'Thomas'
lastname = 'JONES'

@results = doc.css('role').where((doc.css('firstName').text == @firstname) AND (doc.css('lastName').text == @lastname))

查看

<%= @results.each do |t| %>
  <%= t.company %>
<% end %>

所需输出

Jones Enterprises

2 个答案:

答案 0 :(得分:2)

您可以让libXML2基础使用XPath为您完成工作:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<xml>
<role xsi:type="director"> 
 <firstName>Thomas</firstName> 
 <lastName>JONES</lastName>
 <company>Jones Enterprises</company>
</role>
<role xsi:type="director"> 
 <firstName>Thomas</firstName> 
 <lastName>TEST</lastName>
 <company>Test Factory</company>
</role>
</xml>
EOT

FIRSTNAME = 'Thomas'
LASTNAME = 'JONES'

roles = doc.search("//role[child::firstName[text()[contains(., 'Thomas')]] and child::lastName[text()[contains(., 'JONES')]]]")
puts roles.to_xml
# >> <role xsi:type="director"> 
# >>  <firstName>Thomas</firstName> 
# >>  <lastName>JONES</lastName>
# >>  <company>Jones Enterprises</company>
# >> </role>

你可以用CSS做同样的事情,只有CSS不允许我们使用逻辑在同一个libXML调用中测试两个子节点的内容。相反,在那时,我们必须进行多次调用,让Ruby和Nokogiri过滤所需的节点,这将变得更加困难和CPU密集型。这样的事情有效:

roles_firstnames = doc.search('role firstName:contains("Thomas")').map(&:parent)
roles_lastnames = doc.search('role lastName:contains("JONES")').map(&:parent)
matching_roles = (roles_firstnames & roles_lastnames)
puts matching_roles.map(&:to_xml) 
# >> <role xsi:type="director"> 
# >>  <firstName>Thomas</firstName> 
# >>  <lastName>JONES</lastName>
# >>  <company>Jones Enterprises</company>
# >> </role>

注意:

  • Nokogiri让我们使用jQuery提供的大量CSS扩展,例如:contains
  • roles_firstnames & roles_lastnames让Ruby在数组上使用集合交集。每个数组都包含一个包含名字或姓氏的节点列表。每个条目都是父节点的标识符。 &简化了测试,以查看两个数组中的哪些节点是共同的,并且and基本上为我们提供了uniq

无论哪种方式,只要你需要<role>个节点,就可以很容易地迭代它们并提取子<company>节点的文本:

roles.map{ |n| n.at('company').text }
# => ["Jones Enterprises"]

答案 1 :(得分:0)

首先,您选择这样的角色:

@roles = x.css('role').select {|r| firstname == r.at('firstName').text and lastname == r.at('lastName').text }

你应该使用select block中包含filter params的变量。

在您看来,您正在阅读精炼的XML节点,如下所示:

<% @roles.each do |r| %>
  <%= r.at('company').text %>
<% end %>