The 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.

No more invalid JSON from 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.

:include option can be used to include associations with ActiveRecord::Base#to_json

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 }
          }
        }
      }
    }
})

Enumerable#to_json and Hash#to_json now accept options

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).

Unambiguous (non-US-centric) dates and times

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.

What's left?

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 :).