nested to_json objects /w options

There seems to be a limitation on how one can use the options hash with to_json. Take this example:

  • render :json => { :results => @posts.to_json(:only => [:title] ) }* You don’t end up with what you want; instead of results being an array of posts, results becomes an escaped string. I recently put a patch on lighthouse that would cover this specific case but after some further thought, I think a more general solution is in order.

Suppose to_json rather than returning a String instead returned a JSONString. We could then create an encoder for JSONString which does not re-escape itself when to_json is called on it.

You would then be free to do something like the example above and it would all “just work”. You’d also be able to pass different options to different AR collections being returned in the same result.

Are there any other thoughts out there on this?

The problem is that to_json is really the wrong API for providing custom JSON output.

We nearly always want to transform an object into *JSON-encodable primitives*. Not into a JSON string.

So we split the brittle to_json API into as_json and to_json. Rather than implement to_json to return a string, implement as_json to return a JSON-encodable primitive. to_json converts the object using as_json before encoding.

  class Foo     # new way: return a JSON representation     def as_json       { :foo => bar }     end

    # old way: return a JSON string     def to_json(*ugly_internal_options)       { :foo => bar }.to_json(*ugly_internal_options)     end   end

In short, this allows you to do

  render :json => { :results => @posts.as_json(:only => [:title] ) }

jeremy

Ok, that makes sense. Is this rails 3+ only? while the method appears to exist in rails 2.3.5, it thoroughly ignores my options hash.

This is Rails 2.3.6+ (pending release). You can freeze to the 2-3-stable branch to use it now.

jeremy