to_json vs as_json in Rails API

to_json in Rails API Let’s discuss how we started out building our APIs using ‘to_json’. to_json will return JSON string representing the hash. Option are passed to each element. In to_json without any option,  the returned JSON string will include all the model attributes

user = User.find(1)
user.to_json
to_json had some great options of ActiveRecord objects. We could just tell the method to only render certain attributes, or to include association or method calls. We had:
  • only – Only show column names in the output as specified in the list
    user.to_json(:only => [ :id, :email ])
    
  • except – Show all column names except the ones specified in the list
    user.to_json(:except => [:id, :username])
    
to_json works fine and seamlessly ok, as long as the database schema is deeply coupled with the API output.When it takes the next level where we want to render a certain attribute in json it start to break.
render :json => { :sucess => true,
  :user => @user.to_json(:only => [:name]) }
This will start to generate a bit load to controllers. Such methods of generating json don’t feel quite right and begin to break down. This is because the to_json is interested in ‘serializing’ a database object while most of the developers try to put relevant representation for their API. 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, Data, String etc. Now the creation of the json is separated from the rendering of the json.  as_json is used to create the structure of the json as a Hash, and the rendering of that hash into JSON string is left up-to ActiveSupport::json.encode.

as_json in Rails API

as_json Returns a hash representing the model. Like in to_json, option are passed to each element in as_json. The option include_root_in_json controls the top-level behavior of as_json. If true, as_json will emit a single root node named after the object’s type. The default value for include_root_in_json option is false. This behavior can also be achieved by setting the :root  option to true.
user.as_json(root: true)
In as_json without any option, the returned HASH will include all the model attributes
user = User.find(1)
user.as_json
The :only and :except options can be used to limit the attributes included, and work similar to the attributes method.
user.as_json(:only => [ :id, :email ])
user.as_json(:except => [:id, :username])
Default as_json will create a hash which includes all the model attributes. We normally override the as_json to create custom JSON structure.
def as_json(options={})
 { :email => self.email }
end
As we were going through this we also came across a method called from_json. So we decided to write about it as well

from_json in Rails API

from_json will sets the model attributes from a JSON string. The default value for include_root option is false. We can change it to ‘true’ if the given JSON string includes a single root node.
json = { user: { email: '[email protected]', username: 'adone'} }.to_json
user = User.new
user.from_json(json, true)
user.email                 #=> "[email protected]"
user.username              #=> "adone"
Do not call to_json directly, and let the render take it, in controllers. Incase of custom structured or multi level JSON structure override as_json in the model or call as_json. Using as_json will help to worry one less.

References

]]>