Test if a child node exists (without getting NoMethodError)

£可爱£侵袭症+ 提交于 2019-12-10 12:55:51

问题


<root>
  <channel>
    <one>example</one>
    <two>example2</two>
  </channel>
  <channel>
    <one>example</one>
  </channel>
</root>

In the second node, I don't have a <two> node. If I use this: root.channel.two obviously I get the error "Method missing". How can I check to avoid this error? What is the conditional statement I would use?


回答1:


Technique 1: Rescue Any Error

require 'nokogiri'
d = Nokogiri.XML("<foo><bar /></foo>")
bad = d.root.bar            #=> undefined method `bar' for #<...> (NoMethodError)
d.slop!           
yay = d.root.bar            #=> #<... name="bar">
bad = d.root.xxx            #=> undefined method `xxx' for #<...> (NoMethodError)
yay = d.root.xxx rescue nil #=> nil

Technique 2: Look Before Leaping (aka Don't Use Slop)

%w[ bar xxx ].each do |node_name|
  if n = d.root.at_xpath(node_name)
    puts "Yay! #{n}"
  else
    puts "No node named #{node_name}"
  end
end
#=> Yay! <bar/>
#=> No node named xxx

The (no-slop) code some_node.at_xpath("foo") is identical to some_node.foo when using slop, except that it returns nil when no child node with that name exists. Indeed, the implementation of Slop just calls xpath for the element name: if it finds many elements, you get that Nodeset; if it finds only one element, it gives you that; if it finds no elements, it raises the NoMethodError. The important bits look like this:

def method_missing( name )
  list = xpath(name)
  if list.empty?
    super                 # NoMethodError unless someone else handles this
  elsif list.length == 1
    list.first            # Since we only found one element, return that
  else
    list                  # ...otherwise return the whole list
  end
end

Here's what the Nokogiri documents say about Slop (in the footnotes):

Don’t use this.
No, really, don’t use this. If you use it, don’t report bugs.
You’ve been warned!

In general, XPath is way more powerful and faster than slop traversal. For example, if you want to iterate over every <two> node, you can do:

d.xpath('/root/channel/two').each do |two|
  # This will only find nodes that exist
end

If you describe what you really need to do in the end, we can help you craft better code. In my personal opinion Slop is generally a less-effective way to traverse a document.




回答2:


Here is a simple way of doing this:

  xml = Nokogiri::XML(open("http://www.google.com/ig/api?weather=Auckland+New+Zealand"))

  @current_conditions = xml.xpath("//current_conditions")

  if @current_conditions.empty?
    @display_weather = 0
  else
    @display_weather = 1
  end


来源:https://stackoverflow.com/questions/8511586/test-if-a-child-node-exists-without-getting-nomethoderror

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!