I wanted to customize the ActiveRecord attributes which were being serialized by to_json. This is easy enough if you're serializing one record; add the attribute names as arguments of the to_json method. For demonstration purposes, lets use the following ActiveRecord models:
Survey(id: integer, name: string, created_at: datetime)
has_many :questions
Question(id: integer, body: string, survey_id: integer, position: string, created_at: datetime)
belongs_to :survey
And serialize a survey without the timestamp:
@survey.to_json(:only => [:id, :name])
=> {"survey": {"name": "Favorite Foods", "id": 1}}
But to_json also allows you to serialize associated records with the :include option. So if we want to include the Survey questions, it would look like this:
@survey.to_json(:only => [:id, :name], :include => {:questions => {:except => [:id]}})
=> {"survey": {"name": "Favorite Foods", "id": 1, "questions": [{"id": 1}, {"id":2}]}}
But this isn't the output we wanted. You'll see that the survey's :only option overrode the question's :except option. In other words, the parent's options trickle down unless the association options override them. With to_json, :only overrides :except. So, to have full control over the JSON output, I need to pass in a list of approved attrs and methods for every association, which can be a little verbose.
Since I have a list of attributes and methods that I know I want to expose in my JSON by default, I don't want to pass those into to_json every time. So, I made a plugin that lets you define the attributes and methods that are serialized by to_json and to_xml in the class.
class Question < ActiveRecord::Base
inheritable_attributes[:public_serialization_attrs] = ['id', 'name']
end
class Question < ActiveRecord::Base
inheritable_attributes[:public_serialization_attrs] = ['body', 'survey_id']
inheritable_attributes[:public_serialization_methods] << "thumbnail_path"
end
Furthermore, I don't want the parent's serialization options to trickle down to its associations. This plugin also overrides that behavior so every :include tier has it's own options and doesn't inherit the parents' options.
Sample output using the plugin with the above configurations:
@survey.to_json(:include => :questions)
=> {"survey": {"id": 1, "name": "Favorite Foods", "questions": [{"body": "What's your favorite pizza topping?", "survey_id": 1, "thumbnail_path": "/images/question_1.png"}, {"body": "What's your favorite ice cream flavor?", "survey_id": 1, "thumbnail_path": "/images/question_2.png"}]}}
The plugin is available at GitHub: wdlindmeier/Rails-Custom-Include-Serialization-.
This plugin has been tested on Rails version 2.2.2 and 2.3.4.
Comments