如何在Rails中覆盖to_json?


94

更新:

未正确探讨此问题。真正的问题在于render :json

原始问题中的第一个代码粘贴将产生预期的结果。但是,仍然需要注意。请参阅以下示例:

render :json => current_user

一样的

render :json => current_user.to_json

也就是说,render :json不会自动调用to_json与User对象关联的方法。实际上,如果to_jsonUser模型上被覆盖,render :json => @user将生成以下ArgumentError描述。

摘要

# works if User#to_json is not overridden
render :json => current_user

# If User#to_json is overridden, User requires explicit call
render :json => current_user.to_json

这一切对我来说似乎很愚蠢。这似乎是在告诉我在指定类型时render实际上并没有在调用。有人可以解释这里到底发生了什么吗?Model#to_json:json

任何可以帮助我解决这个问题的精灵都可以回答我的另一个问题:如何通过在Rails中组合@ foo.to_json(options)和@ bars.to_json(options)来构建JSON响应


原始问题:

我在SO上看到了其他一些示例,但是我没有做我想要的事情。

我尝试着:

class User < ActiveRecord::Base

  # this actually works! (see update summary above)
  def to_json
    super(:only => :username, :methods => [:foo, :bar])
  end

end

ArgumentError: wrong number of arguments (1 for 0)进去了

/usr/lib/ruby/gems/1.9.1/gems/activesupport-2.3.5/lib/active_support/json/encoders/object.rb:4:in `to_json

有任何想法吗?


您的示例适用于我的一种模型。是否有任何的usernamefoobar方法需要的参数?
乔纳森·朱利安

不,username不是的方法和foobar不需要的方法。我更新了问题以显示错误发生在哪里。
maček

我正在运行1.8.7。您必须打开该文件,然后查看为什么将arg传递给期望args为零的方法。
乔纳森·朱利安

Answers:


214

你得到ArgumentError: wrong number of arguments (1 for 0),因为to_json需要重写一个参数的options哈希值。

def to_json(options)
  ...
end

的更详细的解释to_jsonas_json和渲染:

在ActiveSupport 2.3.3中,as_json添加了解决您遇到的问题的方法。json 的创建应与json的呈现分开。

现在,随时to_json可以在对象as_json上调用,调用来创建数据结构,然后使用将该哈希编码为JSON字符串ActiveSupport::json.encode。所有类型都会发生这种情况:对象,数字,日期,字符串等(请参阅ActiveSupport代码)。

ActiveRecord对象的行为相同。有一个默认的as_json实现,它创建一个包含所有模型属性的哈希。您应该as_json在Model中重写以创建所需的JSON结构as_json就像旧的一样to_json,采用选项哈希,您可以在其中指定要声明性地包含的属性和方法。

def as_json(options)
  # this example ignores the user's options
  super(:only => [:email, :handle])
end

在您的控制器中,render :json => o可以接受字符串或对象。如果是字符串,则作为响应主体传递,如果是对象,to_json则调用它,触发as_json如上所述。

因此,只要您的模型正确地使用as_json覆盖(或不使用)来表示,那么用于显示一个模型的控制器代码应如下所示:

format.json { render :json => @user }

这个故事的寓意是:避免to_json直接打电话,允许render为您这样做。如果您需要调整JSON输出,请致电as_json

format.json { render :json => 
    @user.as_json(:only => [:username], :methods => [:avatar]) }

@Jonathan Julian,这是对的非常有用的解释as_json。正如您在ActiveRecord :: Serialization文档(api.rubyonrails.org/classes/ActiveRecord/…)中所看到的那样,关于此的文档很少(没有)。我会试试这个:)
maček

1
@乔纳森·朱利安(Jonathan Julian),如果我能投票十次,我会的。as_json文档到底在哪里!再次感谢:)
maček

71

如果您在Rails 3中遇到此问题,请重写serializable_hash而不是as_json。这也将免费获取您的XML格式:)

这花了我永远的时间。希望能对某人有所帮助。


1
有谁知道关于该方法的任何出色著作serializable_hash?当我使用它,它改变了我以后的XML输出从它的名字包裹对象(例如“报价”的报价对象“),而不是总是把它包‘<哈希>’。
泰勒科利尔

@TylerCollier,它应该与to_xml
Sam Soffes

感谢您的解决方案!我正在使用ruby2 / rails4,而as_json不适用于嵌套对象,'include'中未调用覆盖的方法,它与serializable_hash一起起作用!
santuxus

请参阅robots.thoughtbot.com/better-serialization-less-as-json,以获取有关为何应替代serializable_hash的解释。
Topher Hunt 2015年

36

对于不想忽略用户选项但也要添加他们的选项的人:

def as_json(options)
  # this example DOES NOT ignore the user's options
  super({:only => [:email, :handle]}.merge(options))
end

希望这对任何人都有帮助:)


1
这是我的方法,除了我默认使用options哈希值,= {}因此在调用时不需要它
mroach

4

不是覆盖to_json,而是覆盖as_json。然后从as_json调用您想要的:

试试这个:

def as_json 
 { :username => username, :foo => foo, :bar => bar }
end

as_json只是为了ActiveResource?
乔纳森·朱利安

显然ActiveRecord :: Serialization具有as_json api.rubyonrails.org/classes/ActiveRecord/Serialization.html
glebm 2010年

@glebm,我尝试了此操作,但得到了相同的结果。我更新了问题以向您显示。
maček

@glebm,我仍然遇到完全相同的错误。即使我这样做,render :json => current_user我也会得到预期的默认结果(User模型的所有属性均为JSON格式)。当我添加as_json方法到我的User模型,并尝试同样的事情,我得到的错误:(
maček

@glebm,谢谢。我知道我做错了。可能值得检查更新的问题。
maček
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.