srlrs20020 srlrs20020 - 9 months ago 45
Ruby Question

How to render nested resources as json in rails if each nested resource could also have resource nested under it?

So in my rails app there are Lists. Each List has_one Item. Each Item has_many Items. So an Item can belong_to another Item. I want to render these resources as JSON where each Item has its child Items nested under it. Since each Item could potentially have child Items of its own, I can't just keep using the include: option because I would not know when the last Item has no children.

So it seems like I would somehow have to use recursion while rendering the JSON, so if an Item has children it's children are nested under it, and if any of those children have children, their children are nested under them.

I am not sure how to do this using to_json and the include option. If I was just rendering the first child Item of the List I would just do this:

respond_to do |format|
format.json { render json: @list.to_json(include: :item) }

But I have no idea how to recursively render the Item children of the :item and their children, and so on.

Another answer suggests using a view object or a gem like RABL for a different problem that is also seems to be too complex for to_json. Is what I'm trying to do too complicated for the to_json method? What is my best option to recursively render child resources as json?

Thanks for the help.

Answer Source

If you can use custom methods for preparing json, this is how you can do that:

class List
  has_one :item

  def custom_json
    json = to_json
    json[:item] = item.custom_json

class Item
  has_many :items # add other options

  def custom_json
    json = to_json.merge(children: [])
    items.each { |item| json[:children] << item.custom_json }

Note: here items refer to the relation you have in Item class for its child items.

And in your controller, you could do

format.json { render json: @list.custom_json }

This would result in json structure:

{ list_attr: value, list_attr_2: value, item: { item_attr: value, children: [{ child_item_attr: value, ... }] } }

Let me know if it helps.