如何使用Ruby和REXML获取XML页面的子节点

时间:2015-02-03 15:55:50

标签: ruby-on-rails ruby xml xpath rexml

我使用的是Ruby 1.9.3版。这是我想从中获取信息的实际XML页面的简单版本。我需要从需要登录凭据的安全网站访问它。我无法使用Nokogiri,因为我无法使用它登录网站。

<root>
  <person>
    <name>Jack</name>
    <age>10</age>
  </person>
  <person>
    <name>Jones</name>
  </person>
  <person>
    <name>Jon</name>
    <age>16</age>
  </person>
</root>

正如您所看到的,标签age有时不会出现。使用REXML和Ruby,我使用以下代码:

agent = Mechanize.new
xml = agent.get("https://securewebsite.com/page.xml")
document = REXML::Document.new(xml.body)

name = XPath.match(document, "//person/name").map {|x| x.text} 
# => ["Jack", "Jones", "Jon"]

age =  XPath.match(document, "//person/age").map {|x| x.text} 
# => ["10", "16"]

问题在于,我无法将age与正确的name相关联,因为索引现在已经出现故障。例如,在索引1处,名称[1]是琼斯,但年龄[1]是16.但这不是真的,因为琼斯的person标签没有年龄标记。

我有没有办法让age数组输出:# => ["10", nil ,"16"]以便我可以将正确的名称与相应的年龄相关联?

还是有更好的方法吗?如果需要进一步解释,请告诉我。

2 个答案:

答案 0 :(得分:3)

问题是我们将年龄和名称视为完全独立的信息集合。我们需要做的是从人那里获取信息作为集合。

xml = "<your xml here />"
doc = Nokogiri::XML(xml)
persons = doc.xpath("//person")
persons_data = persons.map {|person| 
  {
    name: person.xpath("./name").text,
    age: person.xpath("./age").text
  }
}

这会获取人员节点,然后从中获取相关信息,从而得出结果:

puts persons_data.inspect #=> [
                                {:name=>"Jack", :age=>"10"}, 
                                {:name=>"Jones", :age=>""}, 
                                {:name=>"Jon", :age=>"16"}
                              ]

所以要获得你要打电话的第一个人的姓名和年龄

persons_data[0]["name"] #=> "Jack"
persons_data[0]["age"]  #=> "10"

答案 1 :(得分:1)

我会做这样的事情:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<root>
  <person>
    <name>Jack</name>
    <age>10</age>
  </person>
  <person>
    <name>Jones</name>
  </person>
  <person>
    <name>Jon</name>
    <age>16</age>
  </person>
</root>
EOT

people = doc.search('person').each_with_object({}){ |person, h|
  age = person.at('age')
  h[person.at('name').text] = age ? age.text : nil
}

people # => {"Jack"=>"10", "Jones"=>nil, "Jon"=>"16"}

此时,如果我想要年龄,我会使用values

people.values # => ["10", nil, "16"]

检索一个人的年龄是微不足道的:

people['Jon'] # => "16"
people['Jack'] # => "10"

  

当我使用.to_h方法时出现此错误:``block in':undefined method to_h'

我的错误。 to_h不在较旧的Rubies中,但由于我正在生成返回的哈希值,因此不需要它。我调整了上面的代码,该代码适用于任何实现each_with_object的Ruby。