This project is archived and is in readonly mode.
ActiveSupport::JSON.encode is Inconsistent for #as_json and #to_json
-
Jonathan Julian
Another way to describe this:
as_jsonoptions are not passed down to each element of an array of ActiveRecord objects. In the first example, all the attributes of user are rendered, which is not what I expect.format.json { render :json => @users.as_json(:only => [:email]) }I need to do this hack so that only the
emailattribute is rendered.format.json { render :json => @users.collect{ |u| u.as_json(:only => [:email]) } }I can reproduce this with ActiveRecord and ActiveSupport 2.3.5.
-
Jonathan Julian
I think ActiveSupport 2.3.x needs to provide an as_json implementation that calls as_json(options) on each array element. This is not limited to ActiveRecord.
[{"k" => "1", "x" => "1"}, {"k" => "2", "x" => "2"}].as_json(:only => "k") => [{"k"=>"1", "x"=>"1"}, {"k"=>"2", "x"=>"2"}] [{"k" => "1", "x" => "1"}, {"k" => "2", "x" => "2"}].collect { |element| element.as_json(:only => "k") } => [{"k"=>"1"}, {"k"=>"2"}] -
Rizwan Reza
- No changes were found…
-
Ola Tuvesson
I have the same issue (Rails3b3, Ruby 1.9.2):
format.json { render :json => @listings.as_json(:only => [:name]) } => [{id: 1, name: "ACME", type: 3},{id: 2, name: "Apple", type: 7}]And
format.json { render :json => @listings.to_json(:only => [:name]) } => ArgumentError: wrong number of arguments (2 for 1)And
format.json { render :json => @listings.collect{|u| u.as_json(:only => [:name]) }} => [{id: 1, name: "ACME", type: 3},{id: 2, name: "Apple", type: 7}]But
format.json { render :json => @listings.collect{|u| u.to_json(:only => [:name]) }} => ["{"name":"ACME"},{"name":"Apple"}"]Also, as you can see, looping over each element also inserts extra quotes.
-
Huet Bartels
- Importance changed from to High
I am also having the same issues with 3.0.0.beta3 with ruby 1.9.1p378.
-
Neeraj Singh
- Assigned user set to José Valim
@Huet most likely you already know that rails3 does not support ruby 1.9.1. Officially rails3 is supported on ruby 1.9.2 .
Assigning it to Mr. Valim to get his input on the design strategy behind to_json and as_json and to clarify which one end users should be using.
It is my understanding that as_json is implementation detail and end users (not inside rails) should be using to_json.
-
José Valim
- State changed from new to invalid
as_json should return an object to be represented in json. You should always overwrite as_json in your module, but always call to_json.
About the bug, have you tried on Rails master? There was a bug related with the json gem overwriting Rails behavior which was fixed a few days ago. That fixed, for instance, the ArgumentError a few reported above.
I'm glad to reopen if this is still an issue.
-
Jeremy Kemper
- Milestone set to 2.3.9
- State changed from invalid to open
Implementing
as_jsonis right. Usingto_jsonis a mess because JSON libraries use it for their internal encoding API.This is similar to the Marshal library: you implement
_dumpto return a primitive Ruby object to marshal. You're responsible for returning a marshalable representation of your object, not the action marshaled bytes.Similarly for JSON: you implement
as_jsonto return a JSON representation of your object. The actual work of building a JSON string is handled by internal methods.This is a clearer, cleaner separation of responsibility than using a single
to_jsonmethod for both providing JSON representations and for building JSON strings.So yes, this is a bug :)
-
Jeremy Kemper
- State changed from open to duplicate
Reviewing the examples above, this was fixed. Please verify on latest master!
-
José Valim
- State changed from duplicate to open
As JK assigned, if someone wants to backport the fix from master to 2-3-stable, a patch is welcome. The original commit is:
http://github.com/rails/rails/commit/7bd85a8fc2d216a5e2b1d0380df572...
-
Neeraj Singh
- Tag set to to_json
Mr. Valim,
Looks like the patch you provided is not working for me in rails3. Here is the breakdown.
ActiveRecord::Schema.define(:version => 20100630160235) do create_table "users", :force => true do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" end end class User < ActiveRecord::Base end class UsersController < ApplicationController def index User.delete_all User.create(:name => 'John') User.create(:name => 'Mary') @users = User.all respond_to do |format| format.html # index.html.erb format.xml { render :xml => @users } format.json { render :json => @users.as_json(:only => :name) } end end end format.json { render :json => @users.as_json(:only => :name) } [{"user":{"name":"John","created_at":"2010-06-30T16:07:42Z","updated_at":"2010-06-30T16:07:42Z","id":9}},{"user":{"name":"Mary","created_at":"2010-06-30T16:07:42Z","updated_at":"2010-06-30T16:07:42Z","id":10}}] #If I change to to_json from as_json then it is working great format.json { render :json => @users.to_json(:only => :name) } [{"user":{"name":"John"}},{"user":{"name":"Mary"}}] # once again note that I conducted this test on rails edge and it is failing -
Neeraj Singh
my bad. to_json should be used in this case and not as_json. So rails3 is working fine.
back to rails2.
-
Neeraj Singh
I am not able to reproduce the issue in rails 2-3-master branch. Here is what I did
ActiveRecord::Schema.define(:version => 20100630152421) do create_table "users", :force => true do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" end end class User < ActiveRecord::Base def as_json(options={}) { :name => self.name.downcase } end end class UsersController < ApplicationController def index User.delete_all User.create(:name => 'JOHN') User.create(:name => 'MARY') @users = User.all respond_to do |format| format.html # index.html.erb format.xml { render :xml => @users } format.json { render :json => @users } end end end # localhost:3000/user.json results in [{"name":"john"},{"name":"mary"}] -
Jonathan Julian
@Neeraj: Try this: remove the
as_jsondefinition from your AR class, and change your controller to do:format.json { render :json => @users.as_json(:only => [:name]) }You'll see that the resulting JSON includes all 3 attributes of each User, instead of just
:name, which was expected. Callingas_jsonfrom a controller is useful when many different controllers each need a different representation and you don't want to code just one into the model. -
Neeraj Singh
May be I am still not getting as_json vs to_json thing. Based on what JK and JV said, I gather than finally to_json should be invoked on the object. as_json should be used to configure what should be the final output.
As I have show in my example above , I used as_json in the model to configure that I only want name attributes and I am invoking to_json on the final set of collection.
JK/JV help me again please :-)
-
Neeraj Singh
- State changed from open to invalid
- installed latest json gem
- Successfully installed json-1.4.3
- if I use @users.to_json(:only => :name) then every thing works fine.
Whether to use to_json or as_json is little confusing. As JK and JV mentioned above, as_json is means to get to right to_json output. as_json should be used in the model to change the behavior of the model when to_json is invoke.
by invoking @users.as_json(), to_json will NEVER get invoked and that is wrong.
I am closing this issue. If there is any question please feel free to post.
I do believe that there should be a rails guide/blog about when to use as_json and when to use to_json.
-
Jeremy Kemper
Users always call
foo.to_jsonorActiveSupport::JSON.encode(foo)The implementor of
Foowrites anas_json -
Jonathan Julian
Ack! Don't close this!
Remember that the caller can get a hash representation by invoking as_json by hand, passing the options hash. This ticket describes the problem that the
optionsare not being passed to each element of an association! Make my code change as described in comment "June 30th, 2010 @ 05:55 PM" to demonstrate the issue.And here is an outsider's explanation of when to use as_json and to_json: http://jonathanjulian.com/2010/04/rails-to_json-or-as_json/
-
Neeraj Singh
'Jonathan Julian' got everything right.
Regarding the options not being passed , checkout my test.
ActiveRecord::Schema.define(:version => 20100630152421) do create_table "users", :force => true do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" end end class User < ActiveRecord::Base end class UsersController < ApplicationController def index User.delete_all User.create(:name => 'JOHN') User.create(:name => 'MARY') @users = User.all respond_to do |format| format.html # index.html.erb format.xml { render :xml => @users } format.json { render :json => @users } end end end # with format.json { render :json => @users.to_json(:only => :name) } # localhost:3000/user.json results in [{"name":"john"},{"name":"mary"}] # with format.json { render :json => @users.to_json(:only => [:name, :created_at) } # localhost:3000/user.json results in [{"user":{"name":"JOHN","created_at":"2010-06-30T17:59:11Z"}},{"user":{"name":"MARY","created_at":"2010-06-30T17:59:11Z"}}] -
pedz
- Tag changed from to_json to activerecord associations, to_json
I am having problems with associations. They do not seem to honor either the to_json or the as_json methods within the child models.
ActiveRecord::Schema.define(:version => 20100713002653) do create_table "boos", :force => true do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" end create_table "foos", :force => true do |t| t.string "name" t.integer "boo_id" t.datetime "created_at" t.datetime "updated_at" end end class Boo < ActiveRecord::Base has_many :foos end class Foo < ActiveRecord::Base belongs_to :boo def as_json(option = {}) super({ :only => [ :name ] } ) end end class BoosController < ApplicationController def index b1 = Boo.create(:name => "Tom"); b1.foos.create(:name => "Alice"); b1.foos.create(:name => "Jane"); b2 = Boo.create(:name => "George"); b2.foos.create(:name => "Suzy"); b2.foos.create(:name => "Jenny"); render :json => Boo.find(:all).to_json({ :include => [ :foos ] }) end end curl localhost:3000/boos.json [{"boo":{"created_at":"2010-07-13T00:52:35Z","id":1,"name":"Tom","updated_at":"2010-07-13T00:52:35Z","foos":[{"boo_id":1,"created_at":"2010-07-13T00:52:35Z","id":1,"name":"Alice","updated_at":"2010-07-13T00:52:35Z"},{"boo_id":1,"created_at":"2010-07-13T00:52:35Z","id":2,"name":"Jane","updated_at":"2010-07-13T00:52:35Z"}]}},{"boo":{"created_at":"2010-07-13T00:52:35Z","id":2,"name":"George","updated_at":"2010-07-13T00:52:35Z","foos":[{"boo_id":2,"created_at":"2010-07-13T00:52:35Z","id":3,"name":"Suzy","updated_at":"2010-07-13T00:52:35Z"},{"boo_id":2,"created_at":"2010-07-13T00:52:35Z","id":4,"name":"Jenny","updated_at":"2010-07-13T00:52:35Z"}]}}]I would expect all the foos to have only the names.
I also don't understand why the as_json only takes :only and :except. How am I suppose to include associations from a controller if to_json is not suppose to be used?(I'm using Rails 2.3.5)
-
Neeraj Singh
- Milestone changed from 2.3.9 to 3.x
- State changed from invalid to open
- Tag changed from activerecord associations, to_json to activerecord associations, patch, to_json
- Importance changed from High to Low
@pedz Attached is a patch that fixes the issue that you mentioned. Please try it and let us know if it works for you.
-
José Valim
- State changed from open to invalid
Neeraj, the test in your patch was supposed to be calling super(:only => :size), no? I don't think the following is/should be supported:
def as_json { :only => :size } endI don't even think as_json should consider such options as :only and :except. Or you either do it manually:
def as_json super.slice("only") endOr you overwrite to_json to change how to encode it (the representation in as_json is still the same):
def to_json super(:only => :size) end -
José Valim
This is a valid test case for me! And the fix provided in the previous patch looks good to me as well. Could you please bundle then in one package Neeraj? :)
-
Neeraj Singh
Fix provided in previous case is not solving the problem mentioned in the failing test. it is much more complicated.
AM uses serializable_hash and then builds the JSON output. However there is no easy way , at least I have not found, to invoke as_json from within serializable_hash.
Also serializable_hash is a higher level abstraction which is used by both to_xml and to_json. I need to spend some time to understand the existing design before I could come up with a solution. This ticket is in my todo list for today. Will see how it goes :-)
-
pedz
I'm not at edge rails but I will look at the patch. I'm not sure when I'll be able to move.
-
Jeremy Kemper
- Milestone cleared.
-
Jeremy Kemper
- State changed from open to resolved
-
Rohit Arondekar
- Tag changed from activerecord associations, patch, to_json to activerecord, associations, patch, to_json
