I'd like to have my json render in two different ways. I now have my as_json method overridden to display a full object in json form like this:
{
prop1: stuff,
prop2: stuff,
innerthings: {
{
prop1:stuff,
prop2:stuff
}
{
prop1:stuff,
prop2:stuff
}
}
}
And the as_json looks like:
#Renders json
def as_json(options={})
super(
:except => [:created_at, :updated_at],
:include => [{:innerthings = > {
:except => [:created_at, :updated_at]
}}]
)
end
I'd also like to have a second option to render like this:
{
prop1:stuff,
prop2:stuff,
countinnerthings:10
}
current when the code below is used, I get the first render:
respond_to do |format|
format.json { render json: #thing}
end
I'd also like to be able to render with something like as_list that I could use in a case like the below to render just a simple list of the objects.
respond_to do |format|
format.json { render json: #things.as_list }
end
Is there a simple way to do this in ruby on rails?
Instead of rolling your own as_json method. Take a look at ActiveModel Serializers. I think it will satisfy you use-case and help organize your code.
You can just define an as_list method
def as_list
{
prop1: "stuff"
}
end
If you need to use includes etc you can call as_json from your at_list method. As had been said serializers are a better option in general.
Related
I have this method in my controller:
# GET /bios/1
# GET /bios/1.json
def show
if member_session?
#member = MemberPresenter.new(#bio.member)
# I need something here to add a flag to the json response to signal this is a member session.
else
#member = MemberPresenter.new(#bio.member)
end
end
I need to modify the json response to return something like:
{ member: #member, member_session: true }
Thanks in advance!
You can use json param for render functions:
render json: { member: #member, member_session: true }
But it's not the best way to render JSON in rails. I'd recommend you try to use https://github.com/rails-api/active_model_serializers
I'm not sure if you specifically want to return json all the time but here's an alternative to rendering other formats as well:
respond_to do |format|
format.html
format.json { render json: { member: #member, flag: #member.status } }
end
For small and simple objects, doing this is fine, but if you had to drag the associations along, you have the choice of using a serializer, or you could override the to_json method to something like this.
# member.rb
def as_json(options = {})
options = options.merge(
except: [
:updated_at,
:created_at,
],
include: { # Getting associations here
address: {
only: [:street, :zip_code],
include: {
neighbors: { only: :name }
}
}
}
)
super.as_json(options)
end
And finally within the controller, render json: #member.to_json and it will pull all the associations you want with it. This is the lazy man's way of serializing aka what I do :)
At the time my controller looks like this:
def search
#icd4 = Icd4Code.search_full(params[:search]).first(20)
render json: { icd: #icd4.as_json(:only => [:bezeichnung, :nummer, :id])}
end
What i would like to change is that my code does not return #icd4.id as :id but instead #icd4.icd3_code_id as :id
So render json: { icd: #icd4 } would look like this:
{"icd":[{"id":6,"nummer":"A00.1","bezeichnung":"Cholera","icd3_code_id":3,"created_at":"2014-02-28T19:38:20.530Z","updated_at":"2014-02-28T19:38:20.530Z"},{"id":7,"nummer":"A00.1","bezeichnung":"El-Tor-Cholera","icd3_code_id":3,"created_at":"2014-02-28T19:38:20.533Z","updated_at":"2014-02-28T19:38:20.533Z"}]}
My actual code render json: { icd: #icd4.as_json(:only => [:bezeichnung, :nummer, :id])} returns this:
{"icd":[{"id":6,"nummer":"A00.1","bezeichnung":"Cholera"},{"id":7,"nummer":"A00.1","bezeichnung":"El-Tor-Cholera"}]}
And i would like this output:
{"icd":[{"id":3,"nummer":"A00.1","bezeichnung":"Cholera"},{"id":7,"nummer":"A00.1","bezeichnung":"El-Tor-Cholera"}]}
How can i achieve this? Thanks
Without a serializer you can iterate through the items and their keys and rename the key when you find yours.
#icd4 = Icd4Code.search_full(params[:search]).first(20)
data = #icd4.as_json(:only => [:bezeichnung, :nummer, :icd3_code_id]).tap do |icd4_json|
icd4_json.each do |icd4_item|
icd4_item.each do |key|
icd4_item[ 'id' ] = icd4_item.delete( key ) if key == 'icd3_code_id'
end
end
end
render json: { icd4: data }
You should definitely take a look at draper and active_model_serializers gems.
http://railscasts.com/episodes/409-active-model-serializers
http://railscasts.com/episodes/286-draper
although I use Draper in a bit different way then Ryan Bates does, usually I do something like this:
render json: item.decorate.as_json
for example as a simplest solution you could have this class:
class Icd4CodeDecorator < Draper::Decorator
decorates :icd4_code
delegate_all
def as_json(options={})
{
id: icd3_code_id,
bezeichnung: bezeichnung,
nummer: nummer
}
end
end
and then in your controller you could just do:
render json: #icd4.decorate.as_json
Although I think it would be better to keep things correct in as_json method and have id value returned for id property and create a decorator class inherited from Draper::CollectionDecorator and define there your custom method, something like:
class Icd4CodesDecorator < Draper::CollectionDecorator
def as_search_json
object.map do |o|
{
id: icd3_code_id,
bezeichnung: bezeichnung,
nummer: nummer
}
end
end
end
and then in your controller you could do:
render json: Icd4CodesDecorator.new(#icd4).as_search_json
This way you can easily create and maintain any number of versions of json output for your models.
The simplest way is to cleverly change the value something like this
def search
#icd4 = Icd4Code.search_full(params[:search]).first(20)
r = icd: #icd4.as_json(:only => [:bezeichnung, :nummer, :icd3_code_id])
final_value = []
r["icd"].each do |h|
f = {}
h.map do |k,v|
if k == 'icd3_code_id'
f["id"] = v
else
f[k] = v
end
end
final_value << f
end
render json: final_value
end
This is a relatively simple one and I'm pretty sure its just syntax.
Im trying to render multiple objects as json as a response in a controller. So something like this:
def info
#allWebsites = Website.all
#allPages = Page.all
#allElementTypes = ElementType.all
#allElementData = ElementData.all
respond_to do |format|
format.json{render :json => #allWebsites}
format.json{render :json =>#allPages}
format.json{render :json =>#allElementTypes}
format.json{render :json =>#allElementData}
end
end
end
Problem is I'm only getting a single json back and its always the top one. Is there any way to render multiple objects this way?
Or should I create a new object made up of other objects.to_json?
you could actually do it like so:
format.json {
render :json => {
:websites => #allWebsites,
:pages => #allPages,
:element_types => #AllElementTypes,
:element_data => #AllElementData
}
}
in case you use jquery you will need to do something like:
data = $.parseJSON( xhr.responseText );
data.websites #=> #allWebsites data from your controller
data.pages #=> #allPages data from your controller
and so on
EDIT:
answering your question, you don't necessarily have to parse the response, it's just what I usually do. There's a number of functions that do it for you right away, for example:
$.getJSON('/info', function(data) {
var websites = data.websites,
pages = data.pages,
...
});
I've looked at similar posts but can't seem to quite figure it out.
I have the following function which works just fine. The Listing model has a foreign key called price_id which maps to the Price model and its price_range column. Price_id is returned as part of the message object in the JSON response.
How can I return the corresponding price_range value from the association instead of the price_id value (as part of the message obj, and keep the other attributes)?
def update
#listing = Listing.find(params[:listing][:id])
#if params were passed in for updating
if #listing.update_attributes(params[:listing])
#should we return the whole thing or just what's needed?
json_response = {
"success" => #listing.save, #save to DB and assign true/false based on success...
"message" => #listing.attributes #USE attributes to show output the content of the #message obj, and not another object called "message"
}
respond_to do |format|
#json response
format.html { render:json => json_response }
format.xml { render :xml => #listing }
#normal response. Consider leaving this for now?
#format.html { render :action => "detail" } #refresh this page, with new data in it. Consider trying to use redirect instead?
#format.xml { head :ok }
end
end #end if
end
add a method in your Listing model with the price_range and call it in serializable_hash
class Listing
def price_range
price.price_range
end
end
Like explain on comment you can use delegate instead this method :
class Listing
delegate :prince_range, :to => price
end
In you controller you can now do :
json_response = {
"success" => #listing.save, #save to DB and assign true/false based on success...
"message" => #listing.serializable_hash(:methods => [:price_range])
}
Based on what I read in this article, you should be able to do this:
class Listing
def as_json
super(:include => :price)
end
end
Then in your controller:
json_response = {
"success" => #listing.save,
"message" => #listing.as_json
}
If I understand correctly, you want to add #listing.price.price_range value to the "message" ?
If so, try this:
"message" => #listing.attributes[:price_range] = #listing.price.price_range
In my controller i have:
#photo = Photo.find(:all)
respond_to do |format|
...
format.json { render :json => #photo.to_json)
end
so my response looks like:
{
"photo":
{
"updated_at":"2010-10-14T19:12:35Z",
"photo_file_size":206422,
"created_at":"2010-10-14T19:12:01Z"
}
},
{
"photo":
{
"updated_at":"2010-10-16T18:19:38Z",
"photo_file_size":83593,
"created_at":"2010-10-14T19:14:35Z"
}
}
how can i add an additional json key value pair for every photo block?
something like:
"photo":
{
"updated_at":"2010-10-14T19:12:35Z",
"photo_file_size":206422,
"created_at":"2010-10-14T19:12:01Z"
----> "created_at_b":"2010/10/14"
}
maybe :include option?
thanks!
to_json can be made to include the result of any method available on your model. For example you could add the following method to your model:
class Photo < ActiveRecord::Base
def created_at_b
# whatever you want to do
end
end
In your controller you add:
format.json { render :json => #photo.to_json(:methods=>[:created_at_b])
That should return the json that you're after.