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

Ruby 如何从XML文件中获取多个属性?,ruby,xml,nokogiri,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&

我有一个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>
我该怎么做呢?

只要更换线路就行了

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

只要您有一个有效的XML文件,它就可以工作

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

  • 介于
    之间的
    ..
    (我想这是故意的,与您的真实数据无关)

  • 您的XML没有根元素。目前您有两个“根”,元素
    。如果这就是真正的XML文件的组成方式,我认为Nokogiri将只读取第一个节点

我看到您在代码中手动指定属性。如果要以指定的格式获取所有属性,请尝试以下操作:

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
我会这样做:

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
以下是
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选择器。因为我想要所有的“乘客”节点,所以搜索变得很容易,不需要深入父节点链

不过,哈希数组的使用/重用非常困难。如果
: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"}
如果需要动态跟踪乘客节点的所有参数,无论何时添加新参数或删除旧参数:

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"}}

基本上,这将创建节点的散列表示,它可以是好的,也可以是坏的,这取决于您试图对数据执行的操作。

您是对的。实际上一切都很好。我的脚本中确实有错误,但在stackoverflow上我写的都是正确的。“您的XML没有根元素。目前您有两个“根”,元素和。”这是因为XML格式不正确。XML需要根节点。谢谢。我现在找到了关于我的问题的正确答案。不要使用
“//文档//乘客//乘客”
。正确的XPath应该是
“//文档/乘客/乘客”
/
表示“从根开始在任何地方找到它”,因此您反复告诉解析器重新开始搜索。XML示例的格式不正确。XML需要一个根节点,而您有两个。要获取所有属性还是特定属性?如果属性发生更改,您是希望代码动态地反映这一点,还是应该将代码设置为静态,并且必须手动调整以获取更改?谢谢!这个方法帮助我删除了脚本中的许多字符串。解析XML/HTML一开始可能会让人困惑。我发现使用CSS选择器更容易,因为它们更可读,尽管XPath的功能更强。在代码中,在它们之间来回跳转是可以的。
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
array = doc.search('Passenger').map{ |node| 
  {
    id: node['Id'],
    name: node['Name']
  }
}
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"}]
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"}
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"}}