我正在尝试使用Nori将XML文档转换为Ruby哈希。但是,不是接收根元素的集合,而是返回包含该集合的新节点。这就是我在做的事情:
@xml = content_for(:layout)
@hash = Nori.new(:parser => :nokogiri, :advanced_typecasting => false).parse(@xml)
或
@hash = Hash.from_xml(@xml)
@xml
的内容为:
<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
的内容:
{"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"}]}}
相反,我想得到:
{"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?
有关答案 0 :(得分:1)
想象一下只包含相同标签列表的XML,例如
<shoppinglist>
<item>apple</item>
<item>banana</item>
<item>cherry</item>
<item>pear</item>
<shoppinglist>
当您将其转换为哈希值时,使用例如哈希值访问项目非常简单。 hash['shoppinglist']['item'][0]
。但在这种情况下你会期待什么?只是一个阵列?根据您的逻辑,现在可以使用hash['shoppinglist'][0]
访问这些项目,但如果您在容器内部有不同的元素,那该怎么办呢?
<shoppinglist>
<date>2013-01-01</date>
<item>apple</item>
<item>banana</item>
<item>cherry</item>
<item>pear</item>
<shoppinglist>
您现在如何访问这些项目?怎么约会?问题是转换为哈希必须适用于一般情况。
虽然我不认识Nori,但我很确定你所提出的问题并没有被证实,只是因为在考虑一般情况时这没有任何意义。作为替代方案,您仍然可以自己将捆绑数组升级到一个级别:
@hash['bundles'] = @hash['bundles']['bundle']
答案 1 :(得分:1)
解决问题的一般方法并不是很好。
我创建了一个名为ArrayHash的特殊对象。它具有特殊属性,如果in只有一个键,并且该键指向的数据值是一个数组,它会向这些数组元素添加整数键。
因此,如果正常的ruby Hash
字典看起来像
{bundle"=>["0", "1", "A", "B"]}
然后在ArrayHash
dictionaary中看起来像这样
{"bundle"=>["0", "1", "A", "B"], 0=>"0", 1=>"1", 2=>"A", 3=>"B"}
由于附加键的类型为Fixnum
,因此此哈希看起来就像Array
[ "0", "1", "A", "B" ]
除了它还有一个“捆绑”条目,因此其大小为5
以下是强制Nori
使用此特殊字典的代码。
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
然后输出
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"]}}}