递归地将Ruby对象转换为JSON

3
我有一个哈希表需要转储为 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) MultiJson使用Oj默认传递此选项,所以那也可以用。虽然根据Oj文档,我不确定为什么这样做可以。即使没有任何as_hash或to_json方法,它也可以正常工作。

你在使用什么底层库来处理JSON? - nishu
根据文档,递归仅针对数组和哈希执行。您可以将此要求应用于自定义类和 #to_json 方法。 - David Unric
@Nishu - 我已经尝试过原生的json库、Oj和Yajl。虽然在我快速尝试中可能有所遗漏,但它们都没有递归地转储。 - Elmer
@DavidUnric - 除了子类化Hash之外,还有其他的方法可以做到这一点吗? - Elmer
这实际上与递归无关,而是与Ruby对象的编组有关。 - Mark Thomas
1个回答

2

通常我会做以下两件事情之一:

加载JSON的核心扩展:

require 'json/add/core'

我不记得为什么使用“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}}"

添加代码实现从字符串到JSON的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。对于它不了解的类型,我们可以给予一些帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接