我该如何使用Ruby解析这个XML?

3

目前,我有一个名为Food_Display_Table.xml的XML文档,其中的数据格式如下:

<Food_Display_Table>
    <Food_Display_Row>
        <Food_Code>12350000</Food_Code>
        <Display_Name>Sour cream dip</Display_Name>
        ....
        <Solid_Fats>105.64850</Solid_Fats>
        <Added_Sugars>1.57001</Added_Sugars>
        <Alcohol>.00000</Alcohol>
        <Calories>133.65000</Calories>
        <Saturated_Fats>7.36898</Saturated_Fats>
    </Food_Display_Row>
    ...
</Food_Display_Table>

我想以人类可读的格式打印一些信息,就像这样:
-----
Sour cream dip
Calories: 133.65000
Saturated Fats: 7.36898
-----

到目前为止,我尝试了这个方法,但是它没有起作用:
require 'rexml/document'
include REXML

data = Document.new File.new("Food_Display_Table.xml", "r")

data.elements.each("*/*/*") do |foodcode, displayname, portiondefault, portionamount, portiondisplayname, factor, increments, multiplier, grains, wholegrains, orangevegetables, darkgreenvegetables, starchyvegetables, othervegetables, fruits, milk, meats, soy, drybeans, oils, solidfats, addedsugars, alcohol, calories, saturatedfats|
  puts "----"
  puts displayname
  puts "Calories: {calories}"
  puts "Saturated Fats: {saturatedfats}"
  puts "----"
end

请注意,您从未关闭文件句柄,这不是一个好主意(但与您的问题无关)。 - Phrogz
Nokogiri和Hpricot都擅长解析XML/HTML。查看它们的文档,它们都非常易于使用。 - venj
我想知道为什么你会接受那个答案。它没有使用REXML,也没有直接匹配你的输出,而且Nokogiri代码比我的Nokogiri代码更复杂。 - Phrogz
嗨,Phrogz,当我尝试你的Nokogiri代码时,输出结果不正确。我认为它没有考虑到XML中有许多<Food_Display_Row>记录。然后我尝试了d11wtq提交的答案,它运行良好,尽管我必须稍微调整一下输出,但那没什么大不了的。 - rps
@rps 非常好,感谢您的解释。对我来说,它完全考虑到了这一点,但也许我创建的具有多行的 XML 文件(因为您没有提供准确的 XML 示例)可能有所不同。重要的是,您得到了答案。 - Phrogz
2个回答

3

使用Xpath。我倾向于使用Nokogiri,因为我更喜欢它的API。

如果路径是硬编码的:

doc = Nokogiri::XML(xml_string)
doc.xpath(".//Food_Display_Row").each do |node|
  puts "-"*5
  puts "Name: #{node.xpath('.//Display_Name').text}"
  puts "Calories: #{node.xpath('.//Calories').text}"
  puts "Saturated Fats: #{node.xpath('.//Saturated_Fats').text}"
  puts "-"*5
end

或者选择更加简洁的方式。
nodes_to_display = ["Display_Name", "Calories", "Saturated_Fats"]

doc = Nokogiri::XML(xml_string)
doc.xpath(".//Food_Display_Row").each do |node|
  nodes_to_display.each do |node_name|
    if value = node.at_xpath(".//#{node_name}")
      puts "#{node_name}: #{value.text}"
    end
  end
end

1

我会用Nokogiri这样做:

require 'nokogiri' # gem install nokogiri
doc = Nokogiri::XML(IO.read('Food_Display_Table.xml'))

good_fields = %w[ Calories Saturated_Fats ]

puts "-"*5
doc.search("Food_Display_Row").each do |node|
  puts node.at('Display_Name').text
  node.search(*good_fields).each do |node|
    puts "#{node.name.gsub('_',' ')}: #{node.text}" 
  end
  puts "-"*5
end

如果非得使用REXML(以前我很喜欢,但现在更喜欢Nokogiri),则以下内容可行:
require 'rexml/document'
doc = REXML::Document.new( IO.read('Food_Display_Table.xml') )

separator = "-"*15
puts separator
desired = %w[ Calories Saturated_Fats ]
doc.root.elements.each do |row|
  puts REXML::XPath.first( row, 'Display_Name' ).text
  desired.each do |node_name|
    REXML::XPath.each( row, node_name ) do |node|
      puts "#{node_name.gsub('_',' ')}: #{node.text}"
    end
  end
  puts separator
end

#=> ---------------
#=> Sour cream dip
#=> Calories: 133.65000
#=> Saturated Fats: 7.36898
#=> ---------------

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接