I know how to disable the root element globally, a la Rails 3.1 include_root_in_json or by using ActiveRecord::Base.include_root_in_json = false
, but I only want to do this for a few JSON requests (not globally).
So far, I've been doing it like this:
@donuts = Donut.where(:jelly => true)
@coffees = Coffee.all
@breakfast_sandwiches = Sandwich.where(:breakfast => true)
dunkin_donuts_order = {}
dunkin_donuts_order[:donuts] = @donuts
dunkin_donuts_order[:libations] = @coffees
dunkin_donuts_order[:non_donut_food] = @breakfast_sandwiches
Donut.include_root_in_json = false
Coffee.include_root_in_json = false
render :json => dunkin_donuts_order
Donut.include_root_in_json = true
Coffee.include_root_in_json = true
There are about 5 cases where I have to do this, sometimes with more than one model, and it doesn't feel clean at all. I had tried putting this in around_filter
s, but exceptions were breaking the flow, and that was getting hairy as well.
There must be a better way!
The answer is, unfortunately, yes and no.
Yes, what you've done above can arguably be done better. No, Rails won't let you add the root on a per-action basis. The JSON rendering just wasn't built with that sort of flexibility in mind.
That being said, here's what I'd do:
include_root_in_json
to false
for those models which have root depending on the action (such as Donut
and Coffee
above).Override as_json
to allow for greater flexibility. Here's an example:
# in model.rb
def as_json(options = nil)
hash = serializable_hash(options)
if options && options[:root]
hash = { options[:root] => hash }
else
hash = hash
end
end
This example will make it so that you can optionally pass a root but defaults to no root. You could alternatively write it the other way.
as_json
, you'll have to modify your render calls appropriately. So, for Donut
, you'd do render :json => @donut.to_json
.Hope this helps!
You can set include_root_in_json
per model instance, and it won't affect the class's (see class_attribute
in the rails api for a description of this behavior). So you can set a sensible default value on the class level, then set a different value on each instance in the relevant controllers.
Example:
@donuts = Donut.where(:jelly => true).each {|d| d.include_root_in_json = false }
For convenience, you can create a utility method that accepts an array of model instances and sets the value on all of them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With