如何在 Rails 中重写 to_json?

发布于 2024-08-27 18:45:42 字数 1550 浏览 7 评论 0原文


更新:

这个问题没有得到适当的探讨。真正的问题在于render :json

原始问题中粘贴的第一个代码将产生预期的结果。然而,仍然有一个警告。请参阅此示例:

render :json => current_user

不同

render :json => current_user.to_json

也就是说,render :json 不会自动调用与 User 对象关联的 to_json 方法。 事实上,如果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

这对我来说似乎很愚蠢。这似乎告诉我,当指定类型 :json 时,render 实际上并未调用 Model#to_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

我在

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

“任何想法?”中收到 ArgumentError: 错误数量的参数(1 代表 0)


Update:

This issue was not properly explored. The real issue lies within render :json.

The first code paste in the original question will yield the expected result. However, there is still a caveat. See this example:

render :json => current_user

is NOT the same as

render :json => current_user.to_json

That is, render :json will not automatically call the to_json method associated with the User object. In fact, if to_json is being overridden on the User model, render :json => @user will generate the ArgumentError described below.

summary

# 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

This all seems silly to me. This seems to be telling me that render is not actually calling Model#to_json when type :json is specified. Can someone explain what's really going on here?

Any genii that can help me with this can likely answer my other question: How to build a JSON response by combining @foo.to_json(options) and @bars.to_json(options) in Rails


Original Question:

I've seen some other examples on SO, but I none do what I'm looking for.

I'm trying:

class User < ActiveRecord::Base

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

end

I'm getting ArgumentError: wrong number of arguments (1 for 0) in

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

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

呢古 2024-09-03 18:45:42

您将收到 ArgumentError:参数数量错误 (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 来创建数据结构,然后使用 ActiveSupport: 将该哈希编码为 JSON 字符串: json.encode。所有类型都会发生这种情况:对象、数字、日期、字符串等(请参阅 ActiveSupport 代码)。

ActiveRecord 对象的行为方式相同。有一个默认的 as_json 实现,它创建一个包含所有模型属性的哈希。 您应该覆盖模型中的 as_json 以创建所需的 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 }

这个故事的寓意是:避免调用 <直接使用 code>to_json ,允许 render 为您执行此操作。如果您需要调整 JSON 输出,请调用 as_json

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

You are getting ArgumentError: wrong number of arguments (1 for 0) because to_json needs to be overridden with one parameter, the options hash.

def to_json(options)
  ...
end

Longer explanation of to_json, as_json, and rendering:

In ActiveSupport 2.3.3, as_json was added to address issues like the one you have encountered. The creation of the json should be separate from the rendering of the json.

Now, anytime to_json is called on an object, as_json is invoked to create the data structure, and then that hash is encoded as a JSON string using ActiveSupport::json.encode. This happens for all types: object, numeric, date, string, etc (see the ActiveSupport code).

ActiveRecord objects behave the same way. There is a default as_json implementation that creates a hash that includes all the model's attributes. You should override as_json in your Model to create the JSON structure you want. as_json, just like the old to_json, takes an option hash where you can specify attributes and methods to include declaratively.

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

In your controller, render :json => o can accept a string or an object. If it's a string, it's passed through as the response body, if it's an object, to_json is called, which triggers as_json as explained above.

So, as long as your models are properly represented with as_json overrides (or not), your controller code to display one model should look like this:

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

The moral of the story is: Avoid calling to_json directly, allow render to do that for you. If you need to tweak the JSON output, call as_json.

format.json { render :json => 
    @user.as_json(:only => [:username], :methods => [:avatar]) }
暗地喜欢 2024-09-03 18:45:42

如果您在 Rails 3 中遇到此问题,请覆盖 serialized_hash 而不是 as_json。这也将免费获得您的 XML 格式:)

If you're having issues with this in Rails 3, override serializable_hash instead of as_json. This will get your XML formatting for free too :)

携君以终年 2024-09-03 18:45:42

对于那些不想忽略用户选项但又添加他们的选项的人:

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

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

For people who don't want to ignore users options but also add their's:

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

Hope this helps anyone :)

旧时光的容颜 2024-09-03 18:45:42

不是覆盖 to_json,而是覆盖 as_json。
并从 as_json 调用你想要的:

试试这个:

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

Override not to_json, but as_json.
And from as_json call what you want:

Try this:

def as_json 
 { :username => username, :foo => foo, :bar => bar }
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文