0

这是我的简化 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为一个数组,因为<Line>myXML 中有两个节点。但如果 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"}]
}
4

2 回答 2

4

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

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

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

于 2013-06-09T04:09:54.143 回答
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>

从这些描述中,您可以找出其余的。

运行后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"}]}

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

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

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

Nokogiri是一个很好的解析 HTML 和 XML 的工具,然后允许我们搜索、添加、更改或删除节点。它支持 CSS 和 XPath 访问器,所以如果您习惯了 jQuery 或 CSS 的工作原理,或 XPath 表达式,您会很快上手。Nokogiri 的教程是了解其工作原理的良好起点。

于 2013-06-09T05:15:46.163 回答