Chris
Chris

Reputation: 6246

How to return 2 json objects at once?

I have a controller returning a json structure like so:

def show
  # .......
  o_json = deep_object_1_to_json(o)
  render :json => o_json
end

private
def deep_object_1_to_json(o)
  o.to_json(
    :include => {....})
end

Now I need to extend it to return 2 objects. However the obvious solution is giving me problems:

def show
  # .......
  o1_json = deep_object_1_to_json(o)
  o2_json = deep_object_2_to_json(o)
  render :json => 
  {
         :object_1 => o1_json,
         :object_2 => o2_json
  }
end

This returns a json object with 2 strings of escaped json data!

The deep_object_2_to_json functions already have several layers of nested includes so I would rather not have to refactor these into a single function. Is there a way to make this easily extendable to add more objects in the future without the double escaping problem above?

Thanks for any pointers.

Upvotes: 4

Views: 1505

Answers (3)

Frederick Cheung
Frederick Cheung

Reputation: 84132

Sounds like you should be constructing something upon which to_json can easily be called.

The obvious candidate for active record objects is as_json. This does everything that to_json does (include the :include option and so on) except actually turning the object into json. Instead you get back a ruby hash which you can manipulate as you want and then call to_json. For example you could do

render :json => {
  :o1 => object1.as_json(:include => :blah),
  :o2 => object2.as_json(:include => :blah)
}

Upvotes: 7

Thilo
Thilo

Reputation: 17735

If you can refactor your class to return it's JSON via the to_json method, you can simply stick two or more objects into an array and call to_json on the array:

1.9.3-p125 :001 > require 'json'
 => true 
1.9.3-p125 :002 > [{foo: "bar"}, {bar: "foo"}].to_json
 => "[{\"foo\":\"bar\"},{\"bar\":\"foo\"}]" 

Example:

def to_json
  super(include: [:some_association])
end

Upvotes: 1

benzado
benzado

Reputation: 84338

Your controller shouldn't be serializing the object as JSON until right before it hands it off to be rendered.

In other words deep_object_1_to_json should just be deep_object_1. Then you can package both return values into an array or hash and render the as JSON.

def show
  # .......
  o1 = deep_object_1(o)
  o2 = deep_object_2(o)
  render :json =>
  {
         :object_1 => o1,
         :object_2 => o2
  }
end

It might be a pain to change it now, but for the future of your system, you really ought to be doing it this way. JSON is just a format for sending objects over the wire or to disk; none of your code should have any references whatsoever to JSON unless it is passing it off to be rendered.

Upvotes: 1

Related Questions