如何从XML文件中获取多个属性?

时间:2014-11-20 15:35:27

标签: ruby xml nokogiri

我有一个XML文件:

<One>
  <Document Count="1">
    <Customer Id="1" Type="0"/>
    <Passengers>
      <Passenger Seq="1" Id="13" Name="Test Name"/>
      <Passenger Seq="2" Id="14" Name="Test Name4"/>
    </Passengers>
  </Document>
  <Document Count="2">
    <Customer Id="2" Type="0"/>
    <Passengers>
      <Passenger Seq="1" Id="16" Name="Test Name10"/>
      <Passenger Seq="2" Id="18" Name="Test Name30"/>
    </Passengers>
  </Document>
</One>
...
<Two>
  <Document Count="1">
    <User Id="1" Type="0"/>
    <Passengers>
      <Passenger Seq="1" Id="123" Name="Test Name"/>
      <Passenger Seq="2" Id="124" Name="Test Name2"/>
    </Passengers>
  </Document>
  <Document Count="2">
    <Customer Id="2" Type="0"/>
    <Passengers>
      <Passenger Seq="1" Id="1130" Name="Test Name123"/>
      <Passenger Seq="2" Id="1131" Name="Test Name34342"/>
    </Passengers>
  </Document>
</Two>

我的步骤:

array = []
doc = Nokogiri::XML(File.open(file.xml))    
doc_pass = doc.xpath("//Document//Passengers//Passenger")    
doc_pass.each do |pass|
  hash = {}
  hash[:id] = pass['Name'] #???
  array << hash  
end

我希望从乘客那里获得所有属性,例如IdName,并为所有乘客创建哈希值。 例如:

[{ :id => '13', :name => "Test Name"}, { :id => '14', :name => "Test Name4"}, { :id => '16', :name => "Test Name10"}, { :id => '18', :name => "Test Name30"}, { :id => '123', :name => "Test Name"} ... ]

我该怎么做?

3 个答案:

答案 0 :(得分:0)

只需替换

  hash[:id] = pass['Name'] #???

  hash[:id] = pass['Id']
  hash[:name] = pass['Name']

只要您拥有有效的XML文件,它就能正常工作。

您问题中的XML片段无效,原因有两个:

  • ...<One>之间的<Two>(我猜这是有意的,而不是您真实数据的问题)
  • 您的XML没有根元素。目前,您有两个“根”,即元素<One><Two>。如果这就是你的真实XML文件的组成方式,我认为Nokogiri只会读取第一个节点。

答案 1 :(得分:0)

我发现您在代码中手动指定了属性。如果您想以您指定的格式获取所有属性,请尝试以下方法:

array = []
Nokogiri::XML(File.open(file.xml)).xpath("//Document//Passengers//Passenger").each do |x|
  hash = {}
  x.attributes.each do |attribute| # loop through all attributes in the matches found
    hash[attribute[1].name.to_sym] = attribute[1].value
  end
  array << hash
end

array应具有此值:

[{:Seq=>"1", :Id=>"13", :Name=>"Test Name"}, {:Seq=>"2", :Id=>"14", :Name=>"Test Name4"}, {:Seq=>"1", :Id=>"16", :Name=>"Test Name10"}, {:Seq=>"2", :Id=>"18", :Name=>"Test Name30"}]

答案 2 :(得分:0)

我这样做:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<xml>
<One>
  <Document Count="1">
    <Customer Id="1" Type="0"/>
    <Passengers>
      <Passenger Seq="1" Id="13" Name="Test Name"/>
      <Passenger Seq="2" Id="14" Name="Test Name4"/>
    </Passengers>
  </Document>
  <Document Count="2">
    <Customer Id="2" Type="0"/>
    <Passengers>
      <Passenger Seq="1" Id="16" Name="Test Name10"/>
      <Passenger Seq="2" Id="18" Name="Test Name30"/>
    </Passengers>
  </Document>
</One>

<Two>
  <Document Count="1">
    <User Id="1" Type="0"/>
    <Passengers>
      <Passenger Seq="1" Id="123" Name="Test Name"/>
      <Passenger Seq="2" Id="124" Name="Test Name2"/>
    </Passengers>
  </Document>
  <Document Count="2">
    <Customer Id="2" Type="0"/>
    <Passengers>
      <Passenger Seq="1" Id="1130" Name="Test Name123"/>
      <Passenger Seq="2" Id="1131" Name="Test Name34342"/>
    </Passengers>
  </Document>
</Two>
</xml>
EOT

以下是查找所有<Passenger>个节点并获取其数据的方法:

array = doc.search('Passenger').map{ |node| 
  {
    id: node['Id'],
    name: node['Name']
  }
}

这是array的样子:

array
# => [{:id=>"13", :name=>"Test Name"},
#     {:id=>"14", :name=>"Test Name4"},
#     {:id=>"16", :name=>"Test Name10"},
#     {:id=>"18", :name=>"Test Name30"},
#     {:id=>"123", :name=>"Test Name"},
#     {:id=>"124", :name=>"Test Name2"},
#     {:id=>"1130", :name=>"Test Name123"},
#     {:id=>"1131", :name=>"Test Name34342"}]

我正在使用CSS选择器。因为我想要所有的乘客&#34;节点,然后搜索变得容易,并且不需要向下钻取父节点链。

尽管使用/重用了哈希数组很难。如果:id中没有碰撞的可能性,我建议使用常规哈希:

hash = doc.search('Passenger').map{ |node| [node['Id'], node['Name']] }.to_h

hash
# => {"13"=>"Test Name",
#     "14"=>"Test Name4",
#     "16"=>"Test Name10",
#     "18"=>"Test Name30",
#     "123"=>"Test Name",
#     "124"=>"Test Name2",
#     "1130"=>"Test Name123",
#     "1131"=>"Test Name34342"}

如果您需要动态跟踪Passenger节点的所有参数,只要添加新的参数或删除旧的参数:

hash = doc.search('Passenger').map{ |node| 
  [
    node['Id'], 
    node.attribute_nodes.map{ |a| 
      [a.name, a.value] 
    }.to_h 
  ] 
}.to_h

hash
# => {"13"=>{"Seq"=>"1", "Id"=>"13", "Name"=>"Test Name"},
#     "14"=>{"Seq"=>"2", "Id"=>"14", "Name"=>"Test Name4"},
#     "16"=>{"Seq"=>"1", "Id"=>"16", "Name"=>"Test Name10"},
#     "18"=>{"Seq"=>"2", "Id"=>"18", "Name"=>"Test Name30"},
#     "123"=>{"Seq"=>"1", "Id"=>"123", "Name"=>"Test Name"},
#     "124"=>{"Seq"=>"2", "Id"=>"124", "Name"=>"Test Name2"},
#     "1130"=>{"Seq"=>"1", "Id"=>"1130", "Name"=>"Test Name123"},
#     "1131"=>{"Seq"=>"2", "Id"=>"1131", "Name"=>"Test Name34342"}}

基本上,&#l; ll会创建节点的哈希表示,这可能是好的,也可能是坏的,具体取决于您尝试对数据做什么。