How to override to_json in Rails?
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]) }
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 :)
This took me forever to figure out. Hope that helps someone.
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 :)