我想将多个格式相同的XML文件解析为CSV文件。
我在谷歌,nokogiri.org上搜索过,但是我没有找到答案。
我在节点/元素结构方面有10个格式相同的XML文件,它们位于当前目录中。
将XML文件合并到一个XML文件后,我需要提取advisory
节点的特定元素。我想将link
,title
,location
,os -> language -> name
和reference -> name
数据输出到CSV文件。
我的代码只能解析单个XML文档,我希望它能够考虑到1:很多:
# Parse the XML file into a Nokogiri::XML::Document object
@doc = Nokogiri::XML(File.open("file.xml"))
# Gather the 5 specific XML elements out of the 'advisory' top-level node
data = @doc.search('advisory').map { |adv|
[
adv.at('link').content,
adv.at('title').content,
adv.at('location').content,
adv.at('os > language > name').content,
adv.at('reference > name').content
]
}
# Loop through each array element in the object and write out as CSV row
CSV.open('output_file.csv', 'wb') do |csv|
# Explicitly set headers until you figure out how to get them programatically
csv << ['Link', 'Title', 'Location', 'OS Name', 'Reference Name']
data.each do |row|
csv << row
end
end
我尝试更改代码以支持多个XML文件并将它们导入Nokogiri :: XML :: Document对象:
xml_docs = []
Dir.glob("*.xml").each do |file|
xml = Nokogiri::XML(File.new(file))
xml_docs << Nokogiri::XML::Document.new(xml)
end
这成功创建了一个数组xml_docs
,其中包含正确的对象,但我不知道如何将这六个对象转换为单个对象。
这是示例XML。所有XML文件都使用相同的节点/元素结构:
<advisories>
<title> Not relevant </title>
<customer> N/A </customer>
<advisory id="12345">
<link> https://www.google.com </link>
<release_date>2016-04-07</release_date>
<title> The Short Description Would Go Here </title>
<location> Location Name Here </location>
<os>
<product>
<id>98765</id>
<name>Product Name</name>
</product>
<language>
<id>123</id>
<name>en</name>
</language>
</os>
<reference>
<id>00029</id>
<name>Full</name>
<area>Not Defined</area>
</reference>
</advisory>
<advisory id="98765">
<link> https://www.msn.com </link>
<release_date>2016-04-08</release_date>
<title> The Short Description Would Go Here </title>
<location> Location Name Here </location>
<os>
<product>
<id>12654</id>
<name>Product Name</name>
</product>
<language>
<id>126</id>
<name>fr</name>
</language>
</os>
<reference>
<id>00052</id>
<name>Partial</name>
<area>Defined</area>
</reference>
</advisory>
</advisories>
代码利用了Nokogiri :: XML :: Document但是如果Nokogiri :: XML :: Builder能更好地为此工作,我非常愿意相应地调整我的代码。
答案 0 :(得分:0)
我处理解析一个XML文件的第一部分,如下所示:
require 'nokogiri'
doc = Nokogiri::XML(<<EOT)
<advisories>
<advisory id="12345">
<link> https://www.google.com </link>
<title> The Short Description Would Go Here </title>
<location> Location Name Here </location>
<os>
<language>
<name>en</name>
</language>
</os>
<reference>
<name>Full</name>
</reference>
</advisory>
<advisory id="98765">
<link> https://www.msn.com </link>
<release_date>2016-04-08</release_date>
<title> The Short Description Would Go Here </title>
<location> Location Name Here </location>
<os>
<language>
<name>fr</name>
</language>
</os>
<reference>
<name>Partial</name>
</reference>
</advisory>
</advisories>
EOT
注意:这已删除节点,因为它们对问题并不重要。因为它会分散注意力,请删除绒毛。
这是代码的核心:
doc.search('advisory').map{ |advisory|
link = advisory.at('link').text
title = advisory.at('title').text
location = advisory.at('location').text
os_language_name = advisory.at('os > language > name').text
reference_name = advisory.at('reference > name').text
{
link: link,
title: title,
location: location,
os_language_name: os_language_name,
reference_name: reference_name
}
}
这可能是干的,但写的是作为一个例子。
运行它会产生一个哈希数组,可以通过CSV轻松输出:
# => [
{:link=>" https://www.google.com ", :title=>" The Short Description Would Go Here ", :location=>" Location Name Here ", :os_language_name=>"en", :reference_name=>"Full"},
{:link=>" https://www.msn.com ", :title=>" The Short Description Would Go Here ", :location=>" Location Name Here ", :os_language_name=>"fr", :reference_name=>"Partial"}
]
一旦你完成了这项工作,然后将其放入循环的修改版本中以输出CSV并读取XML文件。这是未经测试的,但看起来是正确的:
CSV.open('output_file.csv', 'w',
headers: ['Link', 'Title', 'Location', 'OS Name', 'Reference Name'],
write_headers: true
) do |csv|
Dir.glob("*.xml").each do |file|
xml = Nokogiri::XML(File.read(file))
# parse a file and get the array of hashes
end
# pass the array of hashes to CSV for output
end
请注意,您使用的文件模式为'wb'
。您很少需要使用CSV b
,因为CSV应该是文本格式。如果您确定,您将遇到二进制数据,然后也使用'b'
,但这可能导致包含龙的路径。
另请注意,这是使用read
。 read
不具备可扩展性,这意味着它并不关心文件的大小,它会尝试将其读入内存,无论它是否真正适合。有很多理由可以避免这种情况,但最好的是它会让你的程序瘫痪。如果您的XML文件可能超过系统的可用内存,那么您将要使用Nokogiri支持的SAX解析器进行重写。怎么做是一个不同的问题。
它实际上是一个哈希数组的数组。我不知道我是如何结束的,但我很容易就能使用array.flatten
默想:
foo = [] # => []
foo << [{}] # => [[{}]]
foo.flatten # => [{}]
您可能想要这样做:
foo = [] # => []
foo += [{}] # => [{}]
任何时候我必须使用flatten
我看看是否可以创建数组而不是某个数组的数组。并不是因为它们本身就很糟糕,因为有时候它们非常有用,但你真的想要一系列哈希,所以你知道出了什么问题,flatten
是一种便宜的出路,但使用它也会花费更多的CPU时间。最好找出问题并修复它,最终得到更快/更高效的代码。 (有些人会说这是浪费精力或过早优化,但编写高效代码是一个非常好的特质和目标。)