首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >将XML解析为使用Nori和Nokogiri进行散列,但出现意外结果

将XML解析为使用Nori和Nokogiri进行散列,但出现意外结果
EN

Stack Overflow用户
提问于 2013-03-22 07:39:42
回答 2查看 2.6K关注 0票数 1

我正在尝试使用Nori将XML文档转换为Ruby散列。但不是接收根元素的集合,而是返回包含该集合的新节点。这就是我正在做的事情:

代码语言:javascript
复制
@xml  = content_for(:layout)
@hash = Nori.new(:parser => :nokogiri, :advanced_typecasting => false).parse(@xml)

代码语言:javascript
复制
@hash = Hash.from_xml(@xml)

其中,@xml的内容是:

代码语言:javascript
复制
<bundles>
  <bundle>
    <id>6073</id>
    <name>Bundle-1</name>
    <status>1</status>
    <bundle_type>
      <id>6713</id>
      <name>BundleType-1</name>
    </bundle_type>
    <begin_at nil=\"true\"></begin_at>
    <end_at nil=\"true\"></end_at>
    <updated_at>2013-03-21T23:02:32Z</updated_at>
    <created_at>2013-03-21T23:02:32Z</created_at>
  </bundle>
  <bundle>
    <id>6074</id>
    <name>Bundle-2</name>
    <status>1</status>
    <bundle_type>
      <id>6714</id>
      <name>BundleType-2</name>
    </bundle_type>
    <begin_at nil=\"true\"></begin_at>
    <end_at nil=\"true\"></end_at>
    <updated_at>2013-03-21T23:02:32Z</updated_at>
    <created_at>2013-03-21T23:02:32Z</created_at>
  </bundle>
</bundles>

解析器返回以下格式的@hash

代码语言:javascript
复制
{"bundles"=>{"bundle"=>[{"id"=>"6073", "name"=>"Bundle-1", "status"=>"1", "bundle_type"=>{"id"=>"6713", "name"=>"BundleType-1"}, "begin_at"=>nil, "end_at"=>nil, "updated_at"=>"2013-03-21T23:02:32Z", "created_at"=>"2013-03-21T23:02:32Z"}, {"id"=>"6074", "name"=>"Bundle-2", "status"=>"1", "bundle_type"=>{"id"=>"6714", "name"=>"BundleType-2"}, "begin_at"=>nil, "end_at"=>nil, "updated_at"=>"2013-03-21T23:02:32Z", "created_at"=>"2013-03-21T23:02:32Z"}]}} 

相反,我想要得到:

代码语言:javascript
复制
{"bundles"=>[{"id"=>"6073", "name"=>"Bundle-1", "status"=>"1", "bundle_type"=>{"id"=>"6713", "name"=>"BundleType-1"}, "begin_at"=>nil, "end_at"=>nil, "updated_at"=>"2013-03-21T23:02:32Z", "created_at"=>"2013-03-21T23:02:32Z"}, {"id"=>"6074", "name"=>"Bundle-2", "status"=>"1", "bundle_type"=>{"id"=>"6714", "name"=>"BundleType-2"}, "begin_at"=>nil, "end_at"=>nil, "updated_at"=>"2013-03-21T23:02:32Z", "created_at"=>"2013-03-21T23:02:32Z"}]}

关键是我控制XML,它的结构类似于上面描述的方式。

我的问题也与Does RABL's JSON output not conform to standard? Can it?有关

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-03-22 09:24:04

想象一个只由相同标签列表组成的XML,例如

代码语言:javascript
复制
<shoppinglist>
    <item>apple</item>
    <item>banana</item>
    <item>cherry</item>
    <item>pear</item>
<shoppinglist>

当您将其转换为哈希时,使用例如hash['shoppinglist']['item'][0]访问这些项是非常简单的。但在这种情况下,您会期望什么呢?只是一个数组吗?根据您的逻辑,现在应该可以使用hash['shoppinglist'][0]访问这些项,但如果容器中有不同的元素,例如

代码语言:javascript
复制
<shoppinglist>
    <date>2013-01-01</date>
    <item>apple</item>
    <item>banana</item>
    <item>cherry</item>
    <item>pear</item>
<shoppinglist>

您现在将如何访问这些项目?日期是怎么回事?问题是,到哈希的转换必须在一般情况下工作。

虽然我不知道Nori,但我非常确定你从它那里要求的东西不是烘焙的,只是因为当你考虑一般情况时,这是没有意义的。作为另一种选择,您仍然可以自己将捆绑包数组提升一级:

代码语言:javascript
复制
@hash['bundles'] = @hash['bundles']['bundle']
票数 1
EN

Stack Overflow用户

发布于 2016-09-04 02:18:17

你的问题的一般解决方案不是很好。

我创建了一个特殊的对象,并将其命名为ArrayHash。它有一个特殊的属性,如果在中只有一个键,并且该键指向的数据值是一个数组,它会将整数键添加到这些数组元素中。

因此,如果普通的ruby Hash字典看起来像这样

代码语言:javascript
复制
{bundle"=>["0", "1", "A", "B"]}

那么在ArrayHash字典中应该是这样的

代码语言:javascript
复制
{"bundle"=>["0", "1", "A", "B"], 0=>"0", 1=>"1", 2=>"A", 3=>"B"}

因为额外的键是Fixnum类型的,所以这个散列看起来就像Array

代码语言:javascript
复制
[ "0", "1", "A", "B" ]

除了它还有一个"bundle“条目,所以它的大小是5

下面是强制Nori使用这个特殊字典的代码。

代码语言:javascript
复制
require 'nori'

class Nori
  class ArrayHash < Hash
    def [](a)
      if a.is_a? Fixnum and self.size == 1
        key = self.keys[0]
        self[key][a]
      else
        super
      end
    end
    def inspect
      if self.size == 1 and self.to_a[0][1].class == Array
        p = Hash[self.to_a]
        self.values[0].each.with_index do |v, i|
          p[i] = v
        end
        p.inspect
      else
        super
      end
    end
  end
end

class Nori
  class XMLUtilityNode
    alias :old_to_hash :to_hash
    def to_hash
      ret = old_to_hash
      raise if ret.size != 1
      raise unless ret.class == Hash
      a = ret.to_a[0]
      k, v = a.first, a.last
      if v.class == Hash
        v = ArrayHash[ v.to_a ]
      end
      ret = ArrayHash[ k, v ]
      ret
    end
  end
end


h = Nori.new(:parser => :nokogiri, :advanced_typecasting => false).parse(<<EOF)
<top>
<aundles>
  <bundle>0</bundle>
  <bundle>1</bundle>
  <bundle>A</bundle>
  <bundle>B</bundle>
</aundles>
<bundles>
  <nundle>A</nundle>
  <bundle>A</bundle>
  <bundle>B</bundle>
</bundles>
</top>
EOF

puts "#{h['top']['aundles'][0]} == #{ h['top']['aundles']['bundle'][0]}"
puts "#{h['top']['aundles'][1]} == #{ h['top']['aundles']['bundle'][1]}"
puts "#{h['top']['aundles'][2]} == #{ h['top']['aundles']['bundle'][2]}"
puts "#{h['top']['aundles'][3]} == #{ h['top']['aundles']['bundle'][3]}"

puts h.inspect

然后输出为

代码语言:javascript
复制
0 == 0
1 == 1
A == A
B == B
{"top"=>{"aundles"=>{"bundle"=>["0", "1", "A", "B"], 0=>"0", 1=>"1", 2=>"A", 3=>"B"}, "bundles"=>{"nundle"=>"A", "bundle"=>["A", "B"]}}}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15560182

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档