How to convert a nested hash into XML using Nokogiri

前端 未结 3 722
感动是毒
感动是毒 2021-01-05 01:01

I have many levels of a nested hash like:

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


        
相关标签:
3条回答
  • 2021-01-05 01:17

    How about this?

    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 讨论(0)
  • If you are using Rails you could use the built-in to_xml method.

    c = { :foo => 'bar', :foo1 => { :foo2 => 'bar2', :foo3 => 'bar3', :foo4 => { :foo5 => 'bar5' }}}
    
    xml = c.to_xml
    
    => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<hash>\n  <foo>bar</foo>\n  <foo1>\n    <foo2>bar2</foo2>\n    <foo3>bar3</foo3>\n    <foo4>\n      <foo5>bar5</foo5>\n    </foo4>\n  </foo1>\n</hash>\n"
    
    0 讨论(0)
  • 2021-01-05 01:31

    The accepted is a clean solution, but the below really does 'use' Nokogiri to construct XML from a Hash with special handling for attributes:

    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'}}
                    ]
                }
        })
    

    And the resulting XML output:

    <?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>
    
    0 讨论(0)
提交回复
热议问题