如何在同一列下打印具有相同Xpath的元素(相同的标题)

时间:2014-04-27 01:30:39

标签: ruby xpath

我正在尝试使用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>

1 个答案:

答案 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中的每个值中删除了第一个值。只要有更多值(更多行),循环就会继续运行。