递归地将Ruby对象转储到json

时间:2014-08-05 21:58:47

标签: ruby json

我有一个需要转储到json的哈希,但其中一个值是一个对象:

hash = { a: my_object }

如果我尝试使用类似MultiJson.dump(hash)的东西来转储它,它只会序列化顶级哈希值,而不是更深层次的哈希值,所以我最终得到的是

'{"a": #<...>}'

即使对象已经有to_json,to_hash等方法,也会发生这种情况,MultiJson.dump(my_object)工作正常。

这似乎是JSON库应该做的事情,但我猜他们没有。为什么不?他们不应该/不能有程序化的原因吗?或者我错过了什么?

编辑:

更多的搜索 - 我必须在我的初步测试中弄乱一些东西。 Oj可以使用:compat选项递归转储json:

Oj.dump(my_object, mode: :compat)

使用Oj的MultiJson默认情况下会传递此选项,因此也可以使用。虽然根据Oj的文档,我不确定为什么会这样。即使没有任何as_hash或to_json方法,它也能正常工作。

1 个答案:

答案 0 :(得分:2)

一般来说,我会做两件事之一:

加载JSON的核心扩展:

require 'json/add/core'

我不记得为什么“添加/核心”帮助了正常的require 'json',但确实如此。

或者我向自定义类添加to_hashto_array方法并将它们分解为哈希或数组,然后可以将其传递给JSON类以进行序列化。或者我添加to_json方法并创建一个哈希或数组,然后to_json,返回序列化版本:

class Foo
  def initialize
    @bar = 1
    @baz = [1, 2, 3]
    @hub = {'a' => 0, 'b' => 2}
  end

  def to_h
    {
      'bar' => @bar,
      'baz' => @baz,
      'hub' => @hub
    }
  end

  def to_json
    to_h.to_json
  end
end

require 'json'

Foo.new.to_json # => "{\"bar\":1,\"baz\":[1,2,3],\"hub\":{\"a\":0,\"b\":2}}"

添加代码以对{String}执行from_json,您可以将对象作为JSON发送和接收。或者将该功能添加到initialize方法中,这样如果它看到参数的字符串,它会尝试将该字符串转换回Ruby对象,然后填充值:

class Foo
  def initialize(str = nil)
    if str && String === str
      obj = JSON[str]
      @bar, @baz, @hub = obj.values_at('bar', 'baz', 'hub')
    else
      @bar = 1
      @baz = [1, 2, 3]
      @hub = {'a' => 0, 'b' => 2}
    end
  end

  def to_h
    {
      'bar' => @bar,
      'baz' => @baz,
      'hub' => @hub
    }
  end

  def to_json
    to_h.to_json
  end
end

require 'json'

json_stream = Foo.new.to_json # !> assigned but unused variable - json_stream
new_foo = Foo.new('{"bar":3,"baz":4,"hub":{"x":8,"y":9}}')
# => #<Foo:0x007f9ed4054d60 @bar=3, @baz=4, @hub={"x"=>8, "y"=>9}>

只要JSON知道如何解开特定对象,它就会毫无困难地对其进行to_json。对于那些它不知道的类型,很容易给它一些帮助。