如何将Nokogiri Document对象转换为JSON

时间:2011-06-25 13:25:06

标签: ruby xml json nokogiri

我有一些解析的Nokogiri::XML::Document对象,我想打印为JSON。

我可以将它变成一个字符串,将其解析为哈希,使用active-record或Crack然后使用Hash.to_json;但这既丑陋又依赖于manay库。

有没有更简单的方法?

根据评论中的请求,例如XML <root a="b"><a>b</a></root>可以表示为JSON:

<root a="b"><a>b</a></root> #=> {"root":{"a":"b"}}
<root foo="bar"><a>b</a></root> #=> {"root":{"a":"b","foo":"bar"}}

这也是我现在用Crack得到的。实际上,实体和子标签之间的冲突是一个潜在的问题,但我自己构建了大部分XML,因此最简单的方法是完全避免这些冲突:)

3 个答案:

答案 0 :(得分:42)

这个适用于我:

Hash.from_xml(@nokogiri_object.to_xml).to_json

此方法使用主动支持,因此如果您不使用rails,则手动包含活动支持核心扩展:

require 'active_support/core_ext/hash'

答案 1 :(得分:13)

这是一种方法。正如我的评论所指出的,“正确”的答案取决于你的输出应该是什么。 JSON中没有XML节点的规范表示,因此所涉及的库中没有构建这样的功能:

require 'nokogiri'
require 'json'
class Nokogiri::XML::Node
  def to_json(*a)
    {"$name"=>name}.tap do |h|
      kids = children.to_a
      h.merge!(attributes)
      h.merge!("$text"=>text) unless text.empty?
      h.merge!("$kids"=>kids) unless kids.empty?
    end.to_json(*a)
  end
end
class Nokogiri::XML::Document
  def to_json(*a); root.to_json(*a); end
end
class Nokogiri::XML::Text
  def to_json(*a); text.to_json(*a); end
end
class Nokogiri::XML::Attr
  def to_json(*a); value.to_json(*a); end
end

xml = Nokogiri::XML '<root a="b" xmlns:z="zzz">
  <z:a>Hello <b z:x="y">World</b>!</z:a>
</root>'
puts xml.to_json
{
  "$name":"root",
  "a":"b",
  "$text":"Hello World!",
  "$kids":[
    {
      "$name":"a",
      "$text":"Hello World!",
      "$kids":[
        "Hello ",
        {
          "$name":"b",
          "x":"y",
          "$text":"World",
          "$kids":[
            "World"
          ]
        },
        "!"
      ]
    }
  ]
}

请注意,上面的内容完全忽略了名称空间,这可能是您想要的,也可能不是。


转换为JsonML

这是另一种转换为JsonML的替代方案。虽然这是一个有损转换(它不支持注释节点,DTD或命名空间URL),但设计格式有点“傻”(第一个子元素位于[1][2]根据属性是否存在, 指示元素和属性的名称空间前缀:

require 'nokogiri'
require 'json'
class Nokogiri::XML::Node
  def namespaced_name
    "#{namespace && "#{namespace.prefix}:"}#{name}"
  end
end
class Nokogiri::XML::Element
  def to_json(*a)
    [namespaced_name].tap do |parts|
      unless attributes.empty?
        parts << Hash[ attribute_nodes.map{ |a| [a.namespaced_name,a.value] } ]
      end
      parts.concat(children.select{|n| n.text? ? (n.text=~/\S/) : n.element? })
    end.to_json(*a)
  end
end
class Nokogiri::XML::Document
  def to_json(*a); root.to_json(*a); end
end
class Nokogiri::XML::Text
  def to_json(*a); text.to_json(*a); end
end
class Nokogiri::XML::Attr
  def to_json(*a); value.to_json(*a); end
end

xml = Nokogiri::XML '<root a="b" xmlns:z="zzz">
  <z:a>Hello <b z:x="y">World</b>!</z:a>
</root>'
puts xml.to_json
#=> ["root",{"a":"b"},["z:a","Hello ",["b",{"z:x":"y"},"World"],"!"]]

答案 2 :(得分:0)

如果您尝试将SOAP请求转换为REST,那么该请求也可以使用:

require 'active_support/core_ext/hash'
require 'nokogiri'

xml_string = "<root a=\"b\"><a>b</a></root>"
doc = Nokogiri::XML(xml_string)
Hash.from_xml(doc.to_s)