在嵌套对象中使用自定义to_json方法 [英] Using custom to_json method in nested objects

查看:85
本文介绍了在嵌套对象中使用自定义to_json方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个数据结构,该数据结构使用Ruby标准库中的Set类.我希望能够将我的数据结构序列化为JSON字符串.

I have a data structure that uses the Set class from the Ruby Standard Library. I'd like to be able to serialize my data structure to a JSON string.

默认情况下,Set序列化为数组:

By default, Set serializes as an Array:

>> s = Set.new [1,2,3]
>> s.to_json
=> "[1,2,3]"

在您尝试对其进行反序列化之前,还可以.

Which is fine until you try to deserialize it.

所以我定义了一个自定义的to_json方法:

So I defined a custom to_json method:

class Set
  def to_json(*a)
    {
      "json_class" => self.class.name,
      "data" => {
        "elements" => self.to_a
      }
    }.to_json(*a)
  end

  def self.json_create(o)
    new o["data"]["elements"]
  end
end

哪个作品很棒:

>> s = Set.new [1,2,3]
>> s.to_json
=> "{\"data\":{\"elements\":[1,2,3]},\"json_class\":\"Set\"}"

直到我将Set放入哈希或其他内容之前:

Until I put the Set into a Hash or something:

>> a = { 'set' => s }
>> a.to_json
=> "{\"set\":[1,2,3]}"

有人知道为什么Set嵌套在另一个对象中时为什么我的自定义to_json没有被调用吗?

Any idea why my custom to_json doesn't get called when the Set is nested inside another object?

推荐答案

第一个块用于Rails 3.1(旧版本几乎相同);第二个块用于标准的非Rails JSON.如果是tl; dr,则跳到最后.

The first chunk is for Rails 3.1 (older versions will be pretty much the same); the second chunk is for the standard non-Rails JSON. Skip to the end if tl;dr.

您的问题是Rails会这样做:

Your problem is that Rails does this:

[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
  klass.class_eval <<-RUBY, __FILE__, __LINE__
    # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
    def to_json(options = nil)
      ActiveSupport::JSON.encode(self, options)
    end
  RUBY
end

中的

.特别是,这会将Hash的to_json方法更改为仅一个ActiveSupport::JSON.encode调用.

in active_support/core_ext/object/to_json.rb. In particular, that changes Hash's to_json method into just an ActiveSupport::JSON.encode call.

然后,查看ActiveSupport::JSON::Encoding::Encoder,我们看到以下内容:

Then, looking at ActiveSupport::JSON::Encoding::Encoder, we see this:

def encode(value, use_options = true)
  check_for_circular_references(value) do
    jsonified = use_options ? value.as_json(options_for(value)) : value.as_json
    jsonified.encode_json(self)
  end   
end

因此,所有的Rails JSON编码都通过as_json进行.但是,您并没有为Set定义自己的as_json,而是只是设置to_json,并且当Rails忽略它不使用的内容时会感到困惑.

So all the Rails JSON encoding goes through as_json. But, you're not defining your own as_json for Set, you're just setting up to_json and getting confused when Rails ignores something that it doesn't use.

如果您设置自己的Set#as_json:

class Set
    def as_json(options = { })
        {
            "json_class" => self.class.name,
            "data" => { "elements" => self.to_a }
        }
    end
end

然后,您将在Rails控制台和一般的Rails中得到想要的东西:

then you'll get what you're after in the Rails console and Rails in general:

> require 'set'
> s = Set.new([1,2,3])
> s.to_json
 => "{\"json_class\":\"Set\",\"data\":{\"elements\":[1,2,3]}}"
> h = { :set => s }
> h.to_json
 => "{\"set\":{\"json_class\":\"Set\",\"data\":{\"elements\":[1,2,3]}}}" 

请记住,as_json用于准备要进行JSON序列化的对象,然后to_json会生成实际的JSON字符串. as_json方法通常返回简单的可序列化的数据结构,例如Hash和Array,并在JSON中具有直接类似物.然后,一旦有了类似JSON的结构,就可以使用to_json将其序列化为线性JSON字符串.

Keep in mind that as_json is used to prepare an object for JSON serialization and then to_json produces the actual JSON string. The as_json methods generally return simple serializable data structures, such as Hash and Array, and have direct analogues in JSON; then, once you have something that is structured like JSON, to_json is used to serialize it into a linear JSON string.

当我们查看标准的非Rails JSON库时,我们会看到类似这样的内容:

When we look at the standard non-Rails JSON library, we see things like this:

def to_json(*a)
  as_json.to_json(*a)
end

猴子已打入基本类(符号,时间,日期等)中.再次重申,通常用as_json来实现to_json.在这种环境中,我们需要包括标准的to_json以及上面的as_json集合:

monkey patched into the basic classes (Symbol, Time, Date, ...). So once again, to_json is generally implemented in terms of as_json. In this environment, we need to include the standard to_json as well as the above as_json for Set:

class Set
    def as_json(options = { })
        {
            "json_class" => self.class.name,
            "data" => { "elements" => self.to_a }
        }
    end
    def to_json(*a)
        as_json.to_json(*a)
    end
    def self.json_create(o)
        new o["data"]["elements"]
    end
end

并且我们为解码器提供了您的json_create类方法.正确设置所有内容后,我们会在irb中获得类似的信息:

And we include your json_create class method for the decoder. Once that's all properly set up, we get things like this in irb:

>> s = Set.new([1,2,3])
>> s.as_json
=> {"json_class"=>"Set", "data"=>{"elements"=>[1, 2, 3]}}
>> h = { :set => s }
>> h.to_json
=> "{"set":{"json_class":"Set","data":{"elements":[1,2,3]}}}"


执行摘要:如果您在Rails中,不必担心使用to_json做任何事情,as_json是您想玩的东西.如果您不在Rails中,请在as_json中实现大部分逻辑(尽管文档中有说明),并添加标准的to_json实现(def to_json(*a);as_json.to_json(*a);end).


Executive Summary: If you're in Rails, don't worry about doing anything with to_json, as_json is what you want to play with. If you're not in Rails, implement most of your logic in as_json (despite what the documentation says) and add the standard to_json implementation (def to_json(*a);as_json.to_json(*a);end) as well.

这篇关于在嵌套对象中使用自定义to_json方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆