我正在尝试使用Ruby上的REXML解析XML文件。
我想要的是打印所有值和相应的元素名称作为标题。我有这个问题 是一些节点具有重复出现并具有相同Xpath的子元素,因此对于那些节点 我希望在同一列中打印的元素。然后对于下面的小样本,输出所需的 对于Node_XX的元素将是:
输出我正在寻找:
RepVal|YurVal|CD_val|HJY_val|CD_SubA|CD_SubB
MTSJ|AB01-J|45|01|87|12
||34|11|43|62
到目前为止我的代码是下面的代码,但我不知道如何重复 元素打印在同一列中。
提前感谢您的帮助。
我到目前为止的代码:
#!/usr/bin/env ruby
require 'rexml/document'
include REXML
xmldoc = Document.new File.new("input.xml")
arr_H_Xpath = [] # Array to store only once all Xpath´s (without Xpath repeated)
arr_H_Values = [] # Array for headers (each child element´s name)
arr_Values = [] # Values of each child element.
xmldoc.elements.each("//Node_XYZ") {|element|
element.each_recursive do |child|
if (child.has_text? && child.text =~ /^[[:alnum:]]/) && !arr_H_Xpath.include?(child.xpath.gsub(/\[.\]/,"")) # Check if element has text and Xpath is stored in arr_H_Xpath.
arr_H_Xpath << child.xpath.gsub(/\[.\]/,"") #Remove the [..] for repeated XPaths
arr_H_Values << child.xpath.gsub(/\/\w.*\//,"") #Get only name of child element to use it as header
arr_Values << child.text
end
print arr_H_Values + "|"
arr_H_Values.clear
end
puts arr_Values.join("|")
}
input.xml是:
<TopNode>
<NodeX>
<Node_XX>
<RepCD_valm>
<RepVal>MTSJ</RepVal>
</RepCD_valm>
<RepCD_yur>
<Yur>
<YurVal>AB01-J</YurVal>
</Yur>
</RepCD_yur>
<CodesDif>
<CD_Ranges>
<CD_val>45</CD_val>
<HJY_val>01</HJY_val>
<CD_Sub>
<CD_SubA>87</CD_SubA>
<CD_SubB>12</CD_SubB>
</CD_Sub>
</CD_Ranges>
</CodesDif>
<CodesDif>
<CD_Ranges>
<CD_val>34</CD_val>
<HJY_val>11</HJY_val>
<CD_Sub>
<CD_SubA>43</CD_SubA>
<CD_SubB>62</CD_SubB>
</CD_Sub>
</CD_Ranges>
</CodesDif>
</Node_XX>
<Node_XY>
....
....
....
</Node_XY>
</NodeX>
</TopNode>
答案 0 :(得分:0)
这是解决问题的一种方法。这可能有点不寻常,但我正在试验。 :)
首先,我选择了一个数据结构,可以将标题存储为键,每个键存储多个值以表示其他数据行:MultiMap。它就像一个带有多个键的哈希。
使用multimap,您可以将元素存储为键值对:
data = Multimap.new
doc.xpath('//RepVal|//YurVal|//CD_val|//HJY_val|//CD_SubA|//CD_SubB').each do |elem|
data[elem.name] = elem.inner_text
end
data
的内容是:
{"RepVal"=>["MTSJ"],
"YurVal"=>["AB01-J"],
"CD_val"=>["45", "34"],
"HJY_val"=>["01", "11"],
"CD_SubA"=>["87", "43"],
"CD_SubB"=>["12", "62"]}
如您所见,这是收集创建表所需的所有信息的简单方法。现在只需将其转换为以管道分隔的格式即可。对于此或任何分隔格式,我建议使用CSV
:
out = CSV.generate({col_sep: "|"}) do |csv|
columns = data.keys.to_a.uniq
csv << columns
while !data.values.empty? do
csv << columns.map { |col| data[col].shift }
end
end
输出结果为:
RepVal|YurVal|CD_val|HJY_val|CD_SubA|CD_SubB
MTSJ|AB01-J|45|01|87|12
||34|11|43|62
说明:
CSV.generate
创建一个字符串。如果要直接创建输出文件,请改用CSV.open
。有关详细信息,请参阅CSV class。我添加了col_sep
选项来分隔管道字符而不是默认的逗号。
如果data
是哈希值,那么获取列列表就是关键。但由于它是一个会重复键名的Multimap,我必须在其上调用.to_a.uniq
。然后我使用csv << columns
将它们添加到输出中。
为了创建第二行(以及任何后续行),我们切片并获取data
的每个键的 first 值。这就是data[col].shift
的作用:它实际上从data
中的每个值中删除了第一个值。只要有更多值(更多行),循环就会继续运行。