【问题标题】:How to convert a nested hash into XML using Nokogiri如何使用 Nokogiri 将嵌套哈希转换为 XML
【发布时间】:2019-12-07 12:50:29
【问题描述】:

我有很多层次的嵌套哈希,例如:

 { :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>

【问题讨论】:

  • 在 Rails 中你可以简单地做 hash.to_xml
  • 感谢生态,但我有 标记将它们括起来.. 我不想要它们。有没有一种直接的方式来生成它们而不是字符串操作??

标签: ruby xml hash nokogiri


【解决方案1】:

这个怎么样?

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>"

【讨论】:

  • @Hari: map 迭代哈希,块变量k 包含键和v 值。生成的字符串将被收集到一个数组中,最后将join编成一个字符串。
  • 注意:OP 使用 Nokogiri 指定,但是,考虑到 OP 问题中的哈希值,Nokogiri 并不是获得所需输出的最快路径。这个答案是一个简洁而优雅的选择。
  • NB 此解决方案会为数字键生成无效的 xml。
  • 这个问题应该修改为如何将嵌套散列转换成XML。
【解决方案2】:

接受的是一个干净的解决方案,但下面确实“使用”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>

【讨论】:

    【解决方案3】:

    如果你使用 Rails,你可以使用内置的 to_xml 方法。

    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"
    

    【讨论】:

      猜你喜欢
      • 2016-08-26
      • 1970-01-01
      • 2010-11-16
      • 2015-08-12
      • 2013-11-13
      • 1970-01-01
      • 2019-08-01
      • 2010-12-16
      • 2019-03-21
      相关资源
      最近更新 更多