Rails, Firefox, Anime, Mac
In: JavaScript| Ruby on Rails
10 Oct 2007The last time I wrote about ActiveRecord#to_json on edge Rails, it was missing some key functionality. For one, you couldn’t include any associations. Another thing was you couldn’t do something like this in your controller:
@authors = Author.find(:all)
render :json => @authors.to_json(:only => :name)
Oh and did I mention it wasn’t emitting valid JSON by default?
In the last few weeks, several patches to the Rails trunk have fixed all these issues (most of these patches are already on the Rails 2.0 Preview Release). JSON serialization of ActiveRecord objects is finally on par with XML serialization, and that old Jsonifier plugin I wrote can finally be sent off to the plugin retirement home (though, I am thinking of porting all the JSON patches into Jsonifier to add Rails 2.0-like JSON serialization to Rails 1.2.4, but who knows, I am lazy).
Anyway, back to the new JSON stuff on edge Rails.
Yes it’s true. No more changing of module attribute to get Rails to produce valid JSON. Thanks to Choon Keat with his detailed SVN history snooping, we finally managed to get a patch to fix JSON encoding to quote all hash keys into Rails.
At DHH’s urging, I submitted a patch that added the :include option to ActiveRecord#to_json. Of course, it was a sloppy first submission and DHH pointed that out – a little bit of DRY love and it was really great to see it get committed (I have a soft spot for JSON serialization from Rails, since I’d been trying to achieve the same thing with Jsonifier).
So yes, now you can do:
json = @david.to_json(:include => :posts)
# or even...
json = @david.to_json(
:include => {
:posts => {
:include => {
:taggings => {
:include => {
:tag => { :only => :name }
}
}
}
}
})
One problem with Jsonifier that I often get emails about is how the to_json options don’t work for lists (Enumerables). And why is this important? Well, we often retrieve collections of ActiveRecord objects at a time with AR::B#find so it’s not too much to ask to be able to do this in your controller actions:
@authors = Author.find(:all)
render :json => @authors.to_json(:only => :name)
But of course you couldn’t, so I patched away by changing the to_json methods of Enumerable and Hash (and other types as well) to accept an optional options argument. Enumerables will pass on any options it receives to its elements. Hashes will respect :only and :except (as well as passing on the options to its elements).
While Rails 1.2.3 (and 1.2.4) never supported encoding of Dates, Times and DateTimes, edge Rails has been happily supporting Date, Time and DateTime conversions to JSON since revision 6673. Unfortunately, the date string format is in MM/DD/YYYY format:
Time.now.to_json
=> "\"09/21/2007 12:15:02 UTC\""
Geoff Buesing suggested that Rails should change this to the more unambiguous YYYY/MM/DD format. Finding the right date format is important not only for readability reasons, it’s also important that the date string is directly parsable by the many browser JavaScript implementations using the JavaScript new Date(dateString) function. There is no formal spec of how dates should be represented in JSON, but it’s most convenient that strings representing dates can be directly converted into JavaScript Date objects.
Testing in several browsers and on different platforms, albeit not extensively, we settled on the %Y/%m/%d %H:%M:%S %z (strftime) format (more details in the comments of the Trac ticket).
Now it is:
Time.now.to_json
=> "\"2007/09/21 12:15:02 +0800\""
Much better.
Documentation, for one thing. ActiveRecord::Base#to_json is still undocumented (docfix patch). The to_json methods for Enumerable and Hash need documentation as well (will patch that soon).
ActiveRecord::Base#to_json also doesn’t deal with binary attributes, which can be easily resolved by Base64-encoding them (patch coming soon, it gets slightly complicated when trying to do a from_json). And of course, optimization improvements are always nice (decoding from JSON especially).
So, if you use JSON at all, please come in and help patch the remaining bits before Rails 2.0 rolls out. If you can’t (or more realistically, don’t have the time to) patch Rails yourself, do help out by verifying patches :).
14 Responses to New on edge Rails: JSON serialization of ActiveRecord objects reaches maturity
Better JSON output from Rails with the Jsonifier plugin - redemption in a blog
October 15th, 2007 at 10pm
[...] Edge Rails now does whatever Jsonifier does (and more). Check out my blog post on JSON serialization maturity in edge Rails. This means that this plugin is almost obsolete if you are using edge Rails and don’t require [...]
Riccardo Govoni
October 16th, 2007 at 7am
Hi, I have recently worked with some code which needed ActiveRecord to JSON conversion and viceversa. At the end, I produced my very rough solution, which you can find at this page .
The nice thing is that I am able to convert to_json but also from_json, recovering or creating the activerecord counterpart as needed.
I am too newbie on rails to understand if it can be of any use for anyone, but I accept every kind of comment !
Chu Yeow
October 16th, 2007 at 10am
Edge Rails allows you to do ActiveRecord::Base#from_json as well :).
But, very nice stuff you’ve got there, ever thought about rolling it into a plugin for Rails 1.x?
Riccardo Govoni
October 16th, 2007 at 1pm
Ok, thanks for the heads up about Edge Rails. About the plugin, I never created one, but I will take a look at how to do it and eventually ship it as “extra stuff” within my project.
hubert
November 21st, 2007 at 1pm
i recently updated the restaurant model in my rails app so that it now has two associations, :has_many => :hours and :has_and_belongs_to_many => :cuisines. is there a way to automate json serialization of the restaurant object using the new to_json method?
from the example given, it’s clear you can do single associations and nested single associations, but can you do multiple associations as well?
thanks for any suggestions…
-hubert
Sebastian Winkler
December 5th, 2007 at 12am
Are you saying that instead of ISO 8601 Rails’ JSON will be using some custom home-brew date format, because it seems “more unambiguous” than the crazy American format?!
I am stunned!
Whose idea was that?
Alex Egg
December 12th, 2007 at 1pm
Hey,
I tried to find you on #rubyonrails to ask you this.
Shouldn’t this work:
@sub_category.to_json(:include => [:category, {:subscriptions => { :include => :subscriber }}])
This simply including 2 attributes of sub_category: category and subscriptions. And then includes the subscriber for all subscriptions.
This fails: NoMethodError – The error occurred while evaluating nil.macro):
If I remove the category so it’s like this:
@sub_category.to_json(:include => [{:subscriptions => { :include => :subscriber }}])
The serialization works like a champ. If I add category back and remove the subscribers, it works also:
@sub_category.to_json(:include => [:category, :subscriptions ])
There seems to be something wrong with the combination of an include with an an array item including a higher order association.
What do you say? Is my syntax off?
Chu Yeow
December 12th, 2007 at 1pm
Ah, I believe the syntax you are looking for is this:
Chu Yeow
December 12th, 2007 at 1pm
My bad it should be:
More examples at http://api.rubyonrails.org/classes/ActiveRecord/Serialization.html#M001111
Good point though, I didn’t write a test for this and it is rather tricky syntax.
Gregory
January 5th, 2008 at 6am
Hmmm… I am working with Rails 2.0.2, and the JSON support still seems to be a little rough around the edges. Check this out:
>> ActiveSupport::JSON.decode(ActiveSupport::JSON.encode(“”))
=> “\\u003C\\u003E”
Not idempotent. Oh my!
// Gregory
Alex Egg
January 19th, 2008 at 4pm
Did you get a chance to update the tests for the issue I was describing?
http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/37c42ddec5d45b2
Chu Yeow
January 22nd, 2008 at 11pm
Yup finally did: http://dev.rubyonrails.org/ticket/10895
Brian
March 18th, 2008 at 8pm
Is is possible to cross-domain post JSON? Do you have an examples?
The Irish Penguin » Blog Archive » Quick Example of Serialisation via to_json in Ruby On Rails
June 21st, 2008 at 9pm
[...] Note: There’s some great JSON support since Rails 2 has come around. For more info on this see here. [...]