I have my json serialization working fine
render :json => "#{current_object.serialize(:json, :attributes => [:id, :name])}
But I also want to add further data to the json before it gets set back to the client. Mainly the auth_token.
Googled around like crazy but I can not find what option serialize will take to allow me to append/merge my other data into the JSON.
Hopting to find something like this...
current_object.serialize(:json, :attriubtes => [:id, name], :magic_option => {:form_authenticity_token => "#{form_authenticity_token}"})
You want the :methods key, which works like :attributes, but will include the results of the methods given. In your case:
current_object.to_json(
:attributes => [:id, :name],
:methods => [:form_authenticity_token]
)
For what it's worth, in a recent Rails I hacked together what you want like this:
sr = ActiveRecord::Serialization::Serializer.new(your_object, some_serialization_options).serializable_record
sr['extra'] = my_extra_calculation(some_parameters)
format.json { render :json => sr }
Where your_object is what you want to serialize, some_serialization_options are your standard :include, :only, etc parameters, and my_extra_calculation is whatever you want to do to set the value.
Jimmy
Hacked it in and moving on...
current_object.serialize(:json, :attributes => [:id, :name]).gsub(/\}$/, ", \"form_authenticity_token\": \"#{form_authenticity_token}\"}")
Related
How can I expand associations more than one level deep? Right now I can expand reviews but am not sure how to also expand the patient_profile_id?
class Review
belongs_to :patient_profile
end
render json: doctors.to_json(
:include => {:reviews => {:include => :patient_profile_id }}
)
I'd highly suggest you check out the jbuilder gem. There's a great railscast that explains it's usage.
Basically, you will have to add a jbuilder file into your views, that gives you allot more control over your json.
For your specific use case you'd use something like this:
doctors/index.json.jbuilder
json.doctors #doctors do |json, doctor|
json.(doctor, :id, :name)
json.reviews doctor.reviews do |json, review|
json.(review, :id, :rating, :patient_profile_id)
json.patient_profile review.patient_profile do |json, profile|
json.(profile, :id, :name, ... ) # attributes of the profile
end
end
end
Try to use something like this:
render json: doctors.to_json(
:include => {:reviews => {:include => :patient_profile }}
)
Here you can find detail information how to serialize nested objects.
Check for overriding as_json method
Let's say I have an app that handles a TODO list. The list has finished and unfinished items. Now I want to add two virtual attributes to the list object; the count of finished and unfinished items in the list. I also need these to be displayed in the json output.
I have two methods in my model which fetches the unfinished/finished items:
def unfinished_items
self.items.where("status = ?", false)
end
def finished_items
self.items.where("status = ?", true)
end
So, how can I get the count of these two methods in my json output?
I'm using Rails 3.1
The serialization of objects in Rails has two steps:
First, as_json is called to convert the object to a simplified Hash.
Then, to_json is called on the as_json return value to get the final JSON string.
You generally want to leave to_json alone so all you need to do is add your own as_json implementation sort of like this:
def as_json(options = { })
# just in case someone says as_json(nil) and bypasses
# our default...
super((options || { }).merge({
:methods => [:finished_items, :unfinished_items]
}))
end
You could also do it like this:
def as_json(options = { })
h = super(options)
h[:finished] = finished_items
h[:unfinished] = unfinished_items
h
end
if you wanted to use different names for the method-backed values.
If you care about XML and JSON, have a look at serializable_hash.
With Rails 4, you can do the following -
render json: #my_object.to_json(:methods => [:finished_items, :unfinished_items])
Hope this helps somebody who is on the later / latest version
Another way to do this is add this to your model:
def attributes
super.merge({'unfinished' => unfinished_items, 'finished' => finished_items})
end
This would also automatically work for xml serialization.
http://api.rubyonrails.org/classes/ActiveModel/Serialization.html
Be aware though, you might want use strings for the keys, since the method can not deal with symbols when sorting the keys in rails 3. But it is not sorted in rails 4, so there shouldn't be a problem anymore.
just close all of your data into one hash, like
render json: {items: items, finished: finished, unfinished: unfinished}
I just thought I'd provide this answer for anyone like myself, who was trying to integrate this into an existing as_json block:
def as_json(options={})
super(:only => [:id, :longitude, :latitude],
:include => {
:users => {:only => [:id]}
}
).merge({:premium => premium?})
Just tack .merge({}) on to the end of your super()
This will do, without having to do some ugly overridings. If you got a model List for example, you can put this in your controller:
render json: list.attributes.merge({
finished_items: list.finished_items,
unfinished_items: list.unfinished_items
})
As Aswin listed above, :methods will enable you to return a specific model's method/function as a json attribute, in case you have complex assosiations this will do the trick since it will add functions to the existing model/assossiations :D it will work like a charm if you dont want to redefine as_json
Check this code, and please notice how i'm using :methods as well as :include [N+Query is not even an option ;)]
render json: #YOUR_MODEL.to_json(:methods => [:method_1, :method_2], :include => [:company, :surveys, :customer => {:include => [:user]}])
Overwritting as_json function will be way harder in this scenario (specially because you have to add the :include assossiations manually :/
def as_json(options = { })
end
If you want to render an array of objects with their virtual attributes, you can use
render json: many_users.as_json(methods: [:first_name, :last_name])
where first_name and last_name are virtual attributes defined on your model
In a rails app I have an action that returns a json representation of a collection of different models. It looks something like this:
respond_to :json
def index
#cars = Car.all
#vans = Van.all
respond_with({
:cars => #cars,
:vans => #vans
})
end
However, I want to customise the attributes and methods that are passed to the json object. A bit like:
respond_with({
:cars => #cars.to_json(:only => [:make, :model], :methods => [:full_name]),
:vans => #vans
})
Doing the above, causes the json representation of the "cars" to be escaped as one big string, like:
{
"cars":"[{\"car\":{\"make\":\"Ford\" ... etc
"vans": [{"van":{"make":"Citreon" ... vans not escaped
}
Obviously I'm approaching this the wrong way. Can anyone point me in the right direction?
Since you're nesting the to_json in another Hash, I think you need to use as_json (which returns a Hash instead of a String) instead:
respond_with({
:cars => #cars.as_json(:only => [:make, :model], :methods => [:full_name]),
:vans => #vans
})
I have problems to restrict an as_json include by a dynamic attribute:
#pirates_ships = #current_account.pirates.as_json(:include => {:ships => {:only => [:id, :name]}}, :only => [:id, :last_name])
This for sure gives me all pirates with or without their ships.
But I also need to restrict the ships by e.g. ships.ocean_id
I tried resolving it by includes with conditions:
pirates.includes(:ships).where("ships.ocean_id = ?", #ocean.id).as_json(...)
The restriction works, but now all pirates without a ship are lost.
Also no luck with my own JOIN Syntax.
Any ideas?
Ahoy
UPDATE
My solution so far is to manually eager load. This way I can have my dynamic conditions:
#pirates = #current_account.pirates
#ships = #current_account.ships.where({:pirate_id.in => #pirates, :ocean_id => #ocean.id})
render :json => { :pirates => #pirates.as_json(...), :ships => #ships.as_json(...) }
My Ajax callback can now iterate over :pirates and add for each pirate his ships if any.
(I use a JS template engine clientside to generate the view from the JSON response)
Not very elegant, but performance is important in my case.
I'am still open for better ideas.
I tried dynamic has_many :ships, :conditions => ...
but that's a bit fiddly.
I think your best bet would be altering the #pirates_ships hash after generating it from as_json (I tried multiple variations of includes, etc. and couldn't get anything to work).
#pirates_ships = #current_account.pirates.as_json(:include => :ships)
#pirates_ships.each do |pirate|
pirate[:ships].delete_if{ |ship| ship.ocean_id != #ocean.id }
end
# Now, #pirates_ships should contain ALL pirates, as well as ships for #ocean
When using :methods in to_json, is there a way to rename the key? I'm trying to replace the real id with a base62 version of it, and I want the value of base62_id to have the key :id.
#obj.to_json(
:except => :id
:methods => :base62_id
)
I tried to do
#obj.to_json(
:except => :id
:methods => { :id => :base62_id }
)
but that didn't work.
Any advice?
The to_json serializer uses the name of the method as the key for serialization. So you can't use the methods option for this.
Unfortunately to_json method doesnt acceptblock` parameter, otherwise you could have done something similar to
#obj.to_json(:except => :id) {|json| json.id = base62_id }
So that leaves us with a ugly hack such as:
def to_json(options={})
oid, self.id = self.id, self.base62_id(self.id)
super
ensure
self.id = oid
end
Now to_json will return the expected result.