How to create complex Json response in ruby in rails

29,369

Solution 1

You can render a json.erb file and use it like a normal template:

# file: app/views/your_controller/your_action.json.erb
{
  filed1: <%= @some_var %>,
  field2: <%= @another_var %>,
  fieldN: <%= @yet_another_var %>,
  data: <%= @some_data.to_json.html_safe %>
}

in your controller, call the explicit render with content_type:

render :file => "your_file.json.erb", :content_type => 'application/json'

Solution 2

You can customize the output by implementing the code in your model:

def as_json(options={})
  super(:only => [:name, :email])
end

Then in your controller you can use:

render :json => @user

See "Rails to_json or as_json?" for more info.

Solution 3

You can send options for the JSON format for:

  • :only (explicit list of attributes)
  • :except (attribute exclusion list)
  • :methods (list of methods the execute and include their content)
  • :include (relations)

If you want to always use this JSON output formatting, put them in the as_json method (see Devin's answer):

format.json  { render :json => @users.to_json{:only=>[:name, :email, :id], :methods=>[:last_question_answered], :include=>[:profile] }

Solution 4

Take a look to rabl gem, to customize your view:

# app/views/posts/index.rabl
collection @posts
attributes :id, :title, :subject
child(:user) { attributes :full_name }
node(:read) { |post| post.read_by?(@user) }

That will generate:

[{  post :
  {
    id : 5, title: "...", subject: "...",
    user : { full_name : "..." },
    read : true
  }
}]
Share:
29,369

Related videos on Youtube

Radio TukTuk
Author by

Radio TukTuk

Updated on July 09, 2022

Comments

  • Radio TukTuk
    Radio TukTuk almost 2 years

    I am working on ruby on rails project and I want to add respond to Json.

    One simple way is:--

    def index
      @users = User.all
      respond_to do |format|
        format.html # index.html.erb
        format.xml  { render :xml => @users }
        format.json  { render :json => @users.to_json }
      end
    end
    

    But there are some issues with this:-

    1. I don't want to give the whole user object in json response like password hash and cache counter attributes. Facebook, twitter attributes etc.
    2. I want to add more details in the json object (considering stackoverflow model) like Latest question by each user, latest answer given by each user. Instead of image name stored in db via paperclip, I want to pass full url of the image.

    So the question is how can code json reponse in index.json.erb file like we do in index.html.erb. Format your own json response as per needs.

    EDIT

    def index
      @users = User.all
      respond_to do |format|
        format.html # index.html.erb
        format.xml  { render :xml => @users }
        format.json  { render :file => "index.json.erb", :content_type => 'application/json' }
      end
    end
    

    index.json.erb file:-

        <% @users.each do |user| %>
        {
          first_name: <%= user.first_name %>,
          last_name: <%= user.last_name %>  
        }
    
        <% end %>
    

    Error:--

    template missing.

    PS:-- I am just trying using creating this file. This is just a sample

    EDIT

    enter image description here

    edit

     { { first_name: Mohit , last_name: Jain } { first_name: Sahil Miglani, last_name: } { first_name: Hitesh Bansal, last_name: } { first_name: Sudhanshu, last_name: } { first_name: Saakshi, last_name: } { first_name: Kutta, last_name: } { first_name: bc, last_name: } { first_name: hey, last_name: } { first_name: fewhjfbwfb, last_name: vdsv } } 
    

    EDIT

        [ { first_name: Mohit , last_name: Jain } , { first_name: Sahil Miglani, last_name: } , { first_name: Hitesh Bansal, last_name: } , { first_name: Sudhanshu, last_name: } , { first_name: Saakshi, last_name: } , { first_name: Kutta, last_name: } , { first_name: bc, last_name: } , { first_name: hey, last_name: } , { first_name: fewhjfbwfb, last_name: vdsv } ] 
    

    EDIT, quite close to find out the solution:-

        [
        <% @users.each_with_index do |user,index| %>
        {
            <%= "first_name".to_json.html_safe %>: <%= user.first_name.to_json.html_safe %>,
            <%= "last_name".to_json.html_safe %>: <%= user.last_name.to_json.html_safe %>  
        }
        <% unless index== @users.count - 1%>
        ,
        <% end %>
        <% end %>
        ]
    

    Response:-

        [
    
           -
           {
               first_name: "Mohit "
               last_name: "Jain"
           }
           -
           {
               first_name: "Sahil Miglani"
               last_name: null
           }
           -
           {
               first_name: "Hitesh Bansal"
               last_name: null
           }
           -
           {
               first_name: "Sudhanshu"
               last_name: null
           }
           -
           {
               first_name: "Saakshi"
               last_name: null
           }
           -
           {
               first_name: "Kutta"
               last_name: null
           }
           -
           {
               first_name: "bc"
               last_name: null
           }
           -
           {
               first_name: "hey"
               last_name: null
           }
           -
           {
               first_name: "fewhjfbwfb"
               last_name: "vdsv"
           }
    
        ]
    

    Now all i want is to enclose this each response section in user array

    • Radio TukTuk
      Radio TukTuk almost 13 years
      @YetAnotherGeek What exactly you want to see in user model:- relationships? or attributes?
    • Yet Another Geek
      Yet Another Geek almost 13 years
      I am sorry I didn't completely read your question, with regards to the first partial questions though, you can use :only and :except hashes to filter out the content. You can also include data from other table by using :include. I do not know how to modify data easily though without using the JSON gem. Link.
    • bor1s
      bor1s almost 13 years
      You can generate hash from your object and add other needed fields(keys) to this hash - then pass this hash to render :json => your_hash without explicitly tell .to_json on hash
    • tybro0103
      tybro0103 almost 13 years
      I have a blog post about creating xml responses without the use of an erb file. I believe it could solve your problems: tmatthew.net/blog/rails_to_api
  • Victor Moroz
    Victor Moroz almost 13 years
    JSON is a data structure, not view related, so I don't think generating json via erb makes any sense. This is probably a good example how not to do things.
  • Devin M
    Devin M almost 13 years
    I would not recommend using a template for JSON data. There are builders designed to avoid the repeating template of JSON structures.
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    strange, I got a downvote even if I provided what the user asked :-]
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    @Victor Moroz: I've answered on what the user asked. IF I need to just render json on one response, I don't see any problem in using views.
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    @Devin M: you can always share json rendering on a partial. or just put logic in model. it depends from what you really need.
  • Radio TukTuk
    Radio TukTuk almost 13 years
    what if i want to do some calculations on some data. Is it possible to generate in index.json.erb. If yes then how? any 5 min video screencast
  • Devin M
    Devin M almost 13 years
    You don't use views to render data structures. Rails has as_json for a reason and it is better at creating JSON than a human is. The code you have is of no use in any situation unless you need some odd non-standards compliant JSON, It is definitely not good practice to reinvent the wheel even if it does what the user asked for.
  • Jesse Wolgamott
    Jesse Wolgamott almost 13 years
    Get your @user into the place you want it, and then render it. (perform calculations first, then return as json)
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    @Devin M: it's not about non-standard JSON. yes, I use as_json when I need it, not for just one response or when I have different contexts and data to render. Rails has a "render" method for a reason too, I'm not re-inventing the wheel. Moreover, as_json is heavier than a simple render, just sayin'.
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    @Radio TukTuk: try adding controller name in path: "your_controller/index.json.erb" instead of "index.json.erb"
  • Devin M
    Devin M almost 13 years
    No its not, compare this code format.json { render :json => @users.to_json{:only=>[:name, :email, :id], :methods=>[:last_question_answered], :include=>[:profile] } to your code. I would pick Jesse Wolgamott's solution any day of the week. You should use what rails provides to generate JSON.
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    yes, I really like Jesse Wolgamott's answer (that's why I've upvoted it :). for the rest, I stick on the fact that rails has_many ways to achieve same goals on different context/needs. I just picked the one that (imho) fits with this particular context.
  • Radio TukTuk
    Radio TukTuk almost 13 years
    @AndreaPavoni Yaa the file worked by in firefox its saying:- There was an error parsing the JSON document. The document may not be well-formed. I am using the same code in view file specified in the question
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    @Radio TukTuk: ok, try to see the output generated, then fix JSON syntax in your file. it seems you need to encapsulate serialized users inside a a container (I mean: add "{" and "}" )
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    @Radio TukTuk: please edit your answer and paste the JSON output.
  • Jesse Wolgamott
    Jesse Wolgamott almost 13 years
    @RadioTukTuk: not an issue. have a method called image_url that leverages paperclip (or carrierwave, etc) to create your url for you.
  • Radio TukTuk
    Radio TukTuk almost 13 years
    @AndreaPavoni I got the issue:-- If i do something like <%= @users.to_json %> if then its shows same error.
  • Radio TukTuk
    Radio TukTuk almost 13 years
    @AndreaPavoni I am not able to understand.. why its not rendering json properly.. Like if i do things as every1 else is saying then its rendering json properly.. but if I am rendering this as file its not rendering properly.. Stuck :-(
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    @Radio TukTuk: I need the raw JSON source, not the firefox-rendered version :P it seems it still lacks for brackets to envelop your users' objects
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    ok, you need to enclose with "[" and "]". then separate each user with a comma (I mean: after }, add a comma).
  • Radio TukTuk
    Radio TukTuk almost 13 years
    @andreaPavoni here is the solution <%= @users.to_json.html_safe%>
  • Radio TukTuk
    Radio TukTuk almost 13 years
    @AndreaPavoni I am quite close now. Can you please help me.. Check the last edit part...
  • Andrea Pavoni
    Andrea Pavoni almost 13 years
    ok, edited my answer ;) I didn't use html_safe and it worked well, but my JSON was 1 big container with a set of fields and a list of serialized objects. BTW check that output on other browsers too (sometimes Firefox does bad jokes)
  • migu
    migu almost 11 years
    Great summary of options available for the to_json method. For the to_json method to work, I had to wrap it in brackets (code above: curly bracket and missing closing bracket) like so: format.json { render :json => @users.to_json(:only=>[:name, :email, :id], :methods=>[:last_question_answered], :include=>[:profile]) }
  • hammady
    hammady almost 11 years
    I strongly +1 that this should be the selected answer.