Rails rendering JSON data with Model Root - ruby-on-rails

I've got some data in Rails that I want to render as JSON data. What I'm doing right now is simply finding all instances of a Model and calling render :json=>data.
data = Data.find(:all)
render :json => data
However, Rails is including the model name in each JSON object. So my JSON data ends up looking like this:
[{modelname:{propertyName: 'value',...}},{modelname:{propertyName: 'value2',...}}]
instead of this:
[{propertyName:'value',...},{propertyName:'value2',...}]
The modelname is always the same and I don't want it to be there.
I changed the option to render the root in the JSON data in one of the Rails initializers but that affects everything that I want rendered as JSON, which I don't want to do for this project.
In this case, I want to be able to do this on a case-by-case basis.
How can I do this? Thanks in advance.

With Rails 3, you can use active_model_serializers gem1
that allows you to specify rootless rendering of an object like this:
render :json => data, :root => false

I did not find a way to do this by passing options to the to_json method (and I don't believe there is such an option). You have more alternative to do this, any class that inherits from ActiveRecord::Base will have include_root_in_json.
Do something like this.
Data.include_root_in_json = false
data = Data.find(:all)
render :json => data
Hope this gets you going.
Ok let's try this then.
DataController < ApplicationControlle
private
def custom_json(data)
Data.include_root_in_json = false
data.to_json
Data.include_root_in_json = true
data
end
end
Then your redirect would look like this
data = Data.find(:all)
render :json => custom_json(data)
It's pretty silly code I wish I could think of something else entirely. Let me ask you this: What is it about having the model name included in the json data ?

With Rails 3, I found this way better to do. Override the as_json in your model and do as follows:
def as_json(options = {})
super(options.merge :methods => [:some_method_that_you_want_to_include_result], :include => {:child_relation => {:include => :grand_child_relation } })
end

Related

Params not being saved in rails

Disclaimer: I am very new to Ruby + rails. I'm not sure if this is a bug, but my params variable always seems to be null. I am working on a large and unfamiliar codebase so I'm not sure if it's something else interfering or my own code; any suggestions would be welcome however.
In my routes file I have match '/proxy_request/:number/:ref' => 'proxies#show', via: :get- I was under the impression that this would store :number and :ref variables in params. However when my proxies#show function runs (below), params is an empty hash.
In case it probably is something else interfering with params, is there another way to pass :number and :ref to proxies#show?
class ProxiesController < ApplicationController
include Service
skip_before_action :restrict_access!
def show
binding.pry #params is null here
data = { date: Adapter.staging_date.get(params[:number], params[:ref])}
render json: data, content_type: "application/javascript", callback: #_request.env["QUERY_STRING"].match(/jQuery\d*_\d*/)
end
end
I removed the include Service and all seems to be ok now

How to pass multiple models to ActiveSerializer

My Rails controller action looks something as trivial as the following:
def show
#batter_rankings = DfsHittersBeta.all
#pitcher_rankings = DfsSpBeta.all
render :json => ??
end
In this case, both collections above each have their own serializer. I do want to have them as part of one API. So the API will ultimately look like:
{'pitchers' => [#pitcher_rankings],
'hitters' => [#hitter_rankings]
}
I'm not entirely sure how to pass both models to render as json each with their own serializer though and then perhaps a global serializer which allows me to specify how the final output looks.
You can include both pitchers and hitters in your JSON response like this:
render json: {pitchers: #pitcher_rankings, hitters: #hitter_rankings}

how do I return model parent values when I make a query from the database

So I have a model called Image that belongs_to :user. Each user has a first and last name.
I have a flash app that I am returning a json object back to of Images.
the service I will be calling on the Images controller would look something like this
def getimages
#images = Image.all
render :json => #images
end
My json would look something like this
[{"image":{"created_at":"2011-01-22T19:04:30Z","img_path":"assets/img/bowl_93847566_3_0.png","updated_at":"2011-01-22T19:04:30Z","id":9,"user_id":3}}]
what I would like to do is also include the users first and last name with in the image object that gets passed back.
once I have an image object I am able to do something like image.user.first_name but I am not clear how I would return something like an array of image objects and include the user along with it.
what would be great is if I could get my array of images to look like the following.
[{"image":{"created_at":"2011-01-22T19:04:30Z","img_path":"assets/img/bowl_93847566_3_0.png","updated_at":"2011-01-22T19:04:30Z","id":9,"user_id":3, "first_name":"Matthew", "last_name":"Wallace"}}]
I am thinking this may include adding some kind of model method or somthing that I am not familiar with.
What would be the best practice for achieving this?
You could:
render :json => #images.to_json(:include => :users)
See http://apidock.com/rails/ActiveRecord/Serialization/to_json (and http://apidock.com/rails/Array/to_json shows it works on Arrays). Finally, http://apidock.com/rails/ActionController/Base/render describes using to_json in a json render as optional and not required, which implies it should cause no harm (I couldn't see another way to pass the required options in).
Perhaps cleaner json:
render :json => #images.to_json(:include => { :user => { :only => [:first_name, :last_name] } })
Besides the answer provided by #apneadiving, you can also override the Image's to_json method and return a string containing whatever JSON you need.

How to override to_json in Rails?

Update:
This issue was not properly explored. The real issue lies within render :json.
The first code paste in the original question will yield the expected result. However, there is still a caveat. See this example:
render :json => current_user
is NOT the same as
render :json => current_user.to_json
That is, render :json will not automatically call the to_json method associated with the User object. In fact, if to_json is being overridden on the User model, render :json => #user will generate the ArgumentError described below.
summary
# works if User#to_json is not overridden
render :json => current_user
# If User#to_json is overridden, User requires explicit call
render :json => current_user.to_json
This all seems silly to me. This seems to be telling me that render is not actually calling Model#to_json when type :json is specified. Can someone explain what's really going on here?
Any genii that can help me with this can likely answer my other question: How to build a JSON response by combining #foo.to_json(options) and #bars.to_json(options) in Rails
Original Question:
I've seen some other examples on SO, but I none do what I'm looking for.
I'm trying:
class User < ActiveRecord::Base
# this actually works! (see update summary above)
def to_json
super(:only => :username, :methods => [:foo, :bar])
end
end
I'm getting ArgumentError: wrong number of arguments (1 for 0) in
/usr/lib/ruby/gems/1.9.1/gems/activesupport-2.3.5/lib/active_support/json/encoders/object.rb:4:in `to_json
Any ideas?
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 :)
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 :)
Override not to_json, but as_json.
And from as_json call what you want:
Try this:
def as_json
{ :username => username, :foo => foo, :bar => bar }
end

Pass method chains to to_json

I know you can pass methods the values of which you want to be available to json objects like so:
# user.rb
def name
first_name + last_name
end
# some controller
render :json => #user.to_json(:methods => :name)
But if I want to massage the value returned from the method a bit (with a text helper say) is there a way to do that? I guess another way to ask this is does #to_json support arbitrary attributes? If not, why not? Has anyone else ran into this before?
You can use "render :json" to specify arbitrary attributes in the JSON output. Here is an example:
render :json => { :arbitraryAttribute => arbitrary_method_to_call(), :user => #user.to_json }
The above code would generate JSON like the following:
{
"arbitraryAttribute":"returnValueOfMethodCall",
"user":{ the result of #user.to_json }
}

Resources