如何强制将XML节点解析为哈希数组?

时间:2013-06-09 03:59:14

标签: ruby arrays hash xml-parsing

这是我简化的myXML:

<?xml version="1.0" encoding="utf-8"?>
<ShipmentRequest>
  <Message>
      <MemberId>A00000001</MemberId>
      <MemberName>Bruce</MemberName>
    <Line>
      <LineNumber>3.1</LineNumber>
      <Item>fruit-004</Item>
      <Description>Peach</Description>
    </Line>
    <Line>
      <LineNumber>4.1</LineNumber>
      <Item>fruit-001</Item>
      <Description>Peach</Description>
    </Line>
  </Message>
</ShipmentRequest>

当我使用Crack gem myHash解析它时:

{
   "MemberId"=>"A00000001", 
   "MemberName"=>"Bruce", 
   "Line"=>[
       {"LineNumber"=>"3.1", "Item"=>"A0001", "Description"=>"Apple"}, 
       {"LineNumber"=>"4.1", "Item"=>"A0002", "Description"=>"Peach"}
    ]
}

Crack gem将哈希值Line创建为数组,因为myXML中有两个<Line>个节点。但是如果myXML只包含一个<Line>节点,则Crack gem不会将其解析为数组:

{
    "MemberId"=>"ABC0001", 
    "MemberName"=>"Alan", 
    "Line"=> {"LineNumber"=>"4.1", "Item"=>"fruit-004", "Description"=>"Apple"}
}

无论是否只有一个节点,我都希望它仍然是一个数组:

{
    "MemberId"=>"ABC0001", 
    "MemberName"=>"Alan", 
    "Line"=> [{"LineNumber"=>"4.1", "Item"=>"fruit-004", "Description"=>"Apple"}]
}

2 个答案:

答案 0 :(得分:3)

将XML文档转换为哈希后,您可以执行以下操作:

myHash["Line"] = [myHash["Line"]] if myHash["Line"].kind_of?(Hash)

它将确保Line节点将包装在Array中。

答案 1 :(得分:1)

问题是,你依靠代码来做你真正应该做的事情。 Crack不知道您希望单个节点成为单个元素的数组,而这种行为会让您在尝试深入了解该部分数据时变得更加困难。

解析XML并不难,而且,通过自己解析它,你会知道会发生什么,并且会避免处理“有时它是一个数组,有时它不是”由Crack返回的麻烦。

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<?xml version="1.0" encoding="utf-8"?>
<ShipmentRequest>
  <Message>
      <MemberId>A00000001</MemberId>
      <MemberName>Bruce</MemberName>
    <Line>
      <LineNumber>3.1</LineNumber>
      <Item>fruit-004</Item>
      <Description>Peach</Description>
    </Line>
    <Line>
      <LineNumber>4.1</LineNumber>
      <Item>fruit-001</Item>
      <Description>Peach</Description>
    </Line>
  </Message>
</ShipmentRequest>
EOT

设置DOM,因此可以导航:

hash = {}
message = doc.at('Message')
hash[:member_id] = message.at('MemberId').text
hash[:member_name] = message.at('MemberName').text
lines = message.search('Line').map do |line|
  line_number = line.at('LineNumber').text 
  item = line.at('Item').text 
  description = line.at('Description').text

  {
    :line_number => line_number,
    :item        => item,
    :description => description
  }
end
hash[:lines] = lines
  1. message = doc.at('Message')找到第一个<Message>节点。
  2. message.at('MemberId').text找到<MemberID>内的第一个<Message>节点。
  3. message.at('MemberName').text与上述步骤类似。
  4. message.search('Line')查找<Line>内的所有<Message>个节点。
  5. 从这些描述中你可以弄清楚其余部分。

    运行后,hash看起来像:

    {:member_id=>"A00000001",
    :member_name=>"Bruce",
    :lines=>
      [{:line_number=>"3.1", :item=>"fruit-004", :description=>"Peach"},
      {:line_number=>"4.1", :item=>"fruit-001", :description=>"Peach"}]}
    

    如果我从XML中删除其中一个<Line>块,然后重新运行,我会得到:

    {:member_id=>"A00000001",
    :member_name=>"Bruce",
    :lines=>[{:line_number=>"3.1", :item=>"fruit-004", :description=>"Peach"}]}
    

    使用search定位<Line>节点就是诀窍。 search返回一个NodeSet,它类似于一个数组,所以通过使用map迭代它,它将返回一个内容为<Line>个标签的哈希数组。

    Nokogiri是解析HTML和XML的绝佳工具,然后允许我们搜索,添加,更改或删除节点。它支持CSS和XPath访问器,所以如果你习惯了jQuery或CSS如何工作,或者XPath表达式,你将会快速启动并运行。 Nokogiri的教程是学习它如何工作的良好起点。

相关问题