2012-08-13 35 views
6

我有嵌套的散列的多层次的,如:转换嵌套的散列成XML使用引入nokogiri

{ :foo => 'bar', :foo1 => { :foo2 => 'bar2', :foo3 => 'bar3', :foo4 => { :foo5 => 'bar5' }}} 

我怎样才能将它们转换成XML这样?:

<foo>bar</foo> 
<foo1> 
    <foo2>bar2</foo2> 
    <foo3>bar3</foo3> 
    <foo4> 
     <foo5>bar5</foo5> 
    </foo4> 
</foo1> 

我曾尝试xml.send方法,但它会将上述嵌套散列转换为:

<foo1 foo3="bar3" foo4="foo5bar5" foo2="bar2"/> 
<foo>bar</foo> 
+0

在rails中,你可以简单地做hash.to_xml – ecoologic 2012-08-13 11:49:40

+0

谢谢生态,但我有标签封闭它们..我不想要它们。有没有一种简单的方法来生成它们而不是字符串操作? – Hari 2012-08-13 12:27:03

回答

8

这个怎么样?

class Hash 
  def to_xml 
    map do |k, v| 
      text = Hash === v ? v.to_xml : v 
      "<%s>%s</%s>" % [k, text, k] 
    end.join 
  end 
end 

h.to_xml 
#=> "<foo>bar</foo><foo1><foo2>bar2</foo2><foo3>bar3</foo3><foo4><foo5>bar5</foo5></foo4></foo1>" 
+0

令人敬畏的编码。谢谢:) – Hari 2012-08-13 12:28:52

+0

你可以解释一下地图和end.join在这里做什么? – Hari 2012-08-13 12:39:24

+1

非常优雅的解决方案。 – 2012-08-13 12:49:23

3

接受的是一个干净的解决方案,但下面确实“使用”引入nokogiri从哈希构造XML与属性的特殊处理:

require 'nokogiri' 

def generate_xml(data, parent = false, opt = {}) 
    return if data.to_s.empty? 
    return unless data.is_a?(Hash) 

    unless parent 
     # assume that if the hash has a single key that it should be the root 
     root, data = (data.length == 1) ? data.shift : ["root", data] 
     builder = Nokogiri::XML::Builder.new(opt) do |xml| 
      xml.send(root) { 
       generate_xml(data, xml) 
      } 
     end 

     return builder.to_xml 
    end 

    data.each { |label, value| 
     if value.is_a?(Hash) 
      attrs = value.fetch('@attributes', {}) 
      # also passing 'text' as a key makes nokogiri do the same thing 
      text = value.fetch('@text', '') 
      parent.send(label, attrs, text) { 
       value.delete('@attributes') 
       value.delete('@text') 
       generate_xml(value, parent) 
      } 

     elsif value.is_a?(Array) 
      value.each { |el| 
       # lets trick the above into firing so we do not need to rewrite the checks 
       el = {label => el} 
       generate_xml(el, parent) 
      } 

     else 
      parent.send(label, value) 
     end 
    } 
end 

puts generate_xml(
    {'myroot' => 
     { 
      'num' => 99, 
      'title' => 'something witty', 
      'nested' => { 'total' => [99, 98], '@attributes' => {'foo' => 'bar', 'hello' => 'world'}}, 
      'anothernest' => { 
       '@attributes' => {'foo' => 'bar', 'hello' => 'world'}, 
       'date' => [ 
        'today', 
        {'day' => 23, 'month' => 'Dec', 'year' => {'y' => 1999, 'c' => 21}, '@attributes' => {'foo' => 'blhjkldsaf'}} 
       ] 
      } 
    }}) 
puts puts 
puts generate_xml({ 
      'num' => 99, 
      'title' => 'something witty', 
      'nested' => { 'total' => [99, 98], '@attributes' => {'foo' => 'bar', 'hello' => 'world'}}, 
      'anothernest' => { 
       '@attributes' => {'foo' => 'bar', 'hello' => 'world'}, 
       'date' => [ 
        'today', 
        {'day' => [23,24], 'month' => 'Dec', 'year' => {'y' => 1999, 'c' => 21}, '@attributes' => {'foo' => 'blhjkldsaf'}} 
       ] 
      } 
    }) 

而生成的XML输出:

<?xml version="1.0"?> 
<myroot> 
    <num>99</num> 
    <title>something witty</title> 
    <nested foo="bar" hello="world"> 
    <total>99</total> 
    <total>98</total> 
    </nested> 
    <anothernest foo="bar" hello="world"> 
    <date>today</date> 
    <date foo="blhjkldsaf"> 
     <day>23</day> 
     <month>Dec</month> 
     <year> 
     <y>1999</y> 
     <c>21</c> 
     </year> 
    </date> 
    </anothernest> 
</myroot> 


<?xml version="1.0"?> 
<root> 
    <num>99</num> 
    <title>something witty</title> 
    <nested foo="bar" hello="world"> 
    <total>99</total> 
    <total>98</total> 
    </nested> 
    <anothernest foo="bar" hello="world"> 
    <date>today</date> 
    <date foo="blhjkldsaf"> 
     <day>23</day> 
     <day>24</day> 
     <month>Dec</month> 
     <year> 
     <y>1999</y> 
     <c>21</c> 
     </year> 
    </date> 
    </anothernest> 
</root>