在进行树遍历时排除嵌套元素

时间:2012-06-27 16:03:17

标签: ruby xml tree nokogiri

我正在使用Nokogiri来解析具有(大致)以下结构的XML文件:

<diag>
  <name>A00</name>
  <desc>Cholera</desc>
  <diag>
    <name>A00.0</name>
    <desc>Cholera due to Vibrio cholerae 01, biovar cholerae</desc>
  </diag>
  <diag>
    ...
  </diag>
  ...
</diag>

正如您所看到的,这个树有diag个节点可以任意嵌套,但每个嵌套都是父节点的更具体的描述。

我想“扁平化”这棵树,以便A00.0嵌套A00而不是A00 A00.0 A00.1 ... A00.34 ... A01 ... 我可以只列出类似

的列表
require 'nokogiri'
icd10 = File.new("icd10.xml", "r")
doc = Nokogiri::XML(icd10.read) do |config|
  config.strict.noblanks
end
icd10.close

@diags = {}
@diag_count = 0

def get_diags(node)
  node.children.each do |n|
    if n.name == "diag"
      @diags[@diag_count] = n
      @diag_count += 1
      get_diags(n)
    end
  end
end

# The xml file has sections but what I really want are the contents of the sections
doc.xpath('.//section').each do |n|
  get_diags(n)
end

到目前为止我的样子是这样的:

diag

到目前为止,这确实可以获得文件中的所有@diags[0]元素,但问题是父节点仍然包含在以后的节点中找到的所有内容(例如A00包含A00.0A00.1@diags[1]等节点,而A00.0仅包含get_diags内容。

如何在遍历get_diags中的xml内容时从父元素中排除嵌套元素?提前谢谢!

==编辑==

所以我将此添加到我的def get_diags(node) node.children.each do |n| if n.name == "diag" f = Nokogiri::XML.fragment(n.to_s) f.search('.//diag').children.each do |d| if d.name == "diag" d.remove end end @diags[@diag_count] = f @diag_count += 1 get_diags(n) end end end 方法

@diags

现在<diag>...</diag>拥有xml的片段,其中所有嵌套的{{1}}都被删除了,这在某种意义上就是我想要的,但总的来说这非常非常难看,我想知道是否有人能够分享一个更好的方式来解决这个问题。感谢

1 个答案:

答案 0 :(得分:2)

无论嵌套有多深,xpath'// diag'都会为每个<diag>节点提供依次。然后,您只需提取每个节点的名称 desc 子项的文本值:

diags = doc.xpath('//diag').map do |diag|
  Hash[
    %w(name desc).map do |key|
      [key, diag.xpath(key).text]
    end
  ]
end
pp diags
# => [{"desc"=>"Cholera", "name"=>"A00"},
# =>  {"desc"=>"Cholera due to Vibrio cholerae 01, biovar cholerae",
# =>   "name"=>"A00.0"}]

如果您希望创建具有不同结构的新XML树,我不会费心尝试转换原始树。只需获取提取的数据并使用它来创建新树:

builder = Nokogiri::XML::Builder.new do |xml|
  xml.diagnoses do
  diags.each do |diag|
    xml.diag {
      xml.name = diag['name']
      xml.desc = diag['desc']
    }
  end
  end
end
puts builder.to_xml
# => <?xml version="1.0"?>
# => <diagnoses>
# =>   <diag>
# =>     <name=>A00</name=>
# =>     <desc=>Cholera</desc=>
# =>   </diag>
# =>   <diag>
# =>     <name=>A00.0</name=>
# =>     <desc=>Cholera due to Vibrio cholerae 01, biovar cholerae</desc=>
# =>   </diag>
# => </diagnoses>