How to "pretty" format JSON output in Ruby on Rails

347,235

Solution 1

Use the pretty_generate() function, built into later versions of JSON. For example:

require 'json'
my_object = { :array => [1, 2, 3, { :sample => "hash"} ], :foo => "bar" }
puts JSON.pretty_generate(my_object)

Which gets you:

{
  "array": [
    1,
    2,
    3,
    {
      "sample": "hash"
    }
  ],
  "foo": "bar"
}

Solution 2

The <pre> tag in HTML, used with JSON.pretty_generate, will render the JSON pretty in your view. I was so happy when my illustrious boss showed me this:

<% if @data.present? %>
   <pre><%= JSON.pretty_generate(@data) %></pre>
<% end %>

Solution 3

Thanks to Rack Middleware and Rails 3 you can output pretty JSON for every request without changing any controller of your app. I have written such middleware snippet and I get nicely printed JSON in browser and curl output.

class PrettyJsonResponse
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, response = @app.call(env)
    if headers["Content-Type"] =~ /^application\/json/
      obj = JSON.parse(response.body)
      pretty_str = JSON.pretty_unparse(obj)
      response = [pretty_str]
      headers["Content-Length"] = pretty_str.bytesize.to_s
    end
    [status, headers, response]
  end
end

The above code should be placed in app/middleware/pretty_json_response.rb of your Rails project. And the final step is to register the middleware in config/environments/development.rb:

config.middleware.use PrettyJsonResponse

I don't recommend to use it in production.rb. The JSON reparsing may degrade response time and throughput of your production app. Eventually extra logic such as 'X-Pretty-Json: true' header may be introduced to trigger formatting for manual curl requests on demand.

(Tested with Rails 3.2.8-5.0.0, Ruby 1.9.3-2.2.0, Linux)

Solution 4

If you want to:

  1. Prettify all outgoing JSON responses from your app automatically.
  2. Avoid polluting Object#to_json/#as_json
  3. Avoid parsing/re-rendering JSON using middleware (YUCK!)
  4. Do it the RAILS WAY!

Then ... replace the ActionController::Renderer for JSON! Just add the following code to your ApplicationController:

ActionController::Renderers.add :json do |json, options|
  unless json.kind_of?(String)
    json = json.as_json(options) if json.respond_to?(:as_json)
    json = JSON.pretty_generate(json, options)
  end

  if options[:callback].present?
    self.content_type ||= Mime::JS
    "#{options[:callback]}(#{json})"
  else
    self.content_type ||= Mime::JSON
    json
  end
end

Solution 5

Check out Awesome Print. Parse the JSON string into a Ruby Hash, then display it with ap like so:

require "awesome_print"
require "json"

json = '{"holy": ["nested", "json"], "batman!": {"a": 1, "b": 2}}'

ap(JSON.parse(json))

With the above, you'll see:

{
  "holy" => [
    [0] "nested",
    [1] "json"
  ],
  "batman!" => {
    "a" => 1,
    "b" => 2
  }
}

Awesome Print will also add some color that Stack Overflow won't show you.

Share:
347,235
JP Richardson
Author by

JP Richardson

https://www.exodus.com - all-in-one app to secure, manage and exchange blockchain assets like Bitcoin and Ethereum.

Updated on July 30, 2022

Comments

  • JP Richardson
    JP Richardson almost 2 years

    I would like my JSON output in Ruby on Rails to be "pretty" or nicely formatted.

    Right now, I call to_json and my JSON is all on one line. At times this can be difficult to see if there is a problem in the JSON output stream.

    Is there way to configure to make my JSON "pretty" or nicely formatted in Rails?

  • TheDeadSerious
    TheDeadSerious over 13 years
    Nifty! I've put this into my ~/.irbrc: def json_pp(json) puts JSON.pretty_generate(JSON.parse(json)) end
  • iconoclast
    iconoclast over 12 years
    To make this useful in Rails, it seems that you should give an answer which includes code that can be used in the same context as format.json { render :json => @whatever }
  • lambshaanxy
    lambshaanxy over 12 years
    Surely prettyprinting should only be used for server-side debugging? If you stick the code above in a controller, you'll have tons of useless whitespace in all responses, which isn't even needed for client-side debugging as any tools worth their salt (eg. Firebug) already handle prettyprinting JSON.
  • iconoclast
    iconoclast over 12 years
    @jpatokal: you may consider there to be other better options, but the question was how to get this to work in Rails. Saying "you don't want to do that in Rails" is a non-answer. Obviously a lot of people want to do it in Rails.
  • lambshaanxy
    lambshaanxy over 12 years
    The original poster said nothing about where in a Rails app he wants to use this, so I answered with a line of Ruby that will work anywhere. To use it to generate the JSON response in a Rails controller, you already answered your own question: format.json { render :json => JSON.pretty_generate(my_json) }.
  • Ammo Goettsch
    Ammo Goettsch over 10 years
    How are you getting around ActiveSupport's redefinition of to_json? This keeps me from pretty printing while ActiveSupport is present.
  • gertas
    gertas over 10 years
    I don't care really, to_json, as_json, jbuilder which I use mostly - whatever, middleware transforms any JSON output. I try to avoid opening classes whenever possible.
  • Sheharyar
    Sheharyar over 10 years
    @jpatokal's comment above for render didn't work for me. I had to modify it a bit: render :json => JSON.pretty_generate(JSON.parse(my_json))
  • lambshaanxy
    lambshaanxy over 10 years
    If you have to call JSON.parse on it, your my_json is a string, not JSON!
  • Kimmo Lehto
    Kimmo Lehto over 10 years
    I had to change the parse line to obj = JSON.parse(response.body.first) to make it work.
  • gertas
    gertas over 10 years
    Just tried your advice and it failed as body is a string under both Thin and Passenger in development mode. RoR3/4 and Ruby 2.0. The response.body is apidock.com/rails/v3.2.13/ActionDispatch/Response/body
  • elsurudo
    elsurudo over 10 years
    Works great in Rails 4 as well... thanks! I prefer this to the more library-specific methods (as in the accepted answer). Since you should only use this in dev mode anyways, the performance hit isn't a big deal.
  • Matthew Brown
    Matthew Brown almost 10 years
    I like this one, and I am looking forward to using it.
  • Wayne Conrad
    Wayne Conrad over 9 years
    See stackoverflow.com/a/26491790/238886 for an evolution of this solution that does not call response.body and should work in any Rack app.
  • nornagon
    nornagon about 9 years
    This is awesome, but it actually causes dates/times to render differently: gist.github.com/nornagon/9c24b68bd6d3e871add3
  • Christopher Oezbek
    Christopher Oezbek almost 9 years
    Several problems with this: (1) JSON.pretty_generate requires json.respond_to?(:to_h) or :to_hash. (2) pretty_generate can choke on things that to_json does not.
  • counterbeing
    counterbeing about 8 years
    Working wonderfully for me with rails 4.2.5 and Puma as my server. Absolutely lovely. Thank you!
  • panteo
    panteo almost 8 years
    In Rails 5 I had to change Rack::Utils.bytesize(pretty_str).to_s to pretty_str.bytesize.to_s and it works great!
  • Johnny Wong
    Johnny Wong over 7 years
    to get a string from pp instead of printing to standard output, use User.first.as_json.pretty_inspect. Works well for me.
  • Xavier
    Xavier over 7 years
    Fantastic, thanks! I made a gem out of this: github.com/zeiv/rails_pretty_json
  • Lev Lukomsky
    Lev Lukomsky over 6 years
    There is also handy method in console jj, which pretty prints json object to STDOUT
  • ConorSheehan1
    ConorSheehan1 over 5 years
    @nornagon I haven't applied this change and I'm getting the same difference you saw between .to_json and pretty_generate. I only see it in a rails console, not plain irb. I think this could be a general rails thing, nothing to do with this patch. Also, Time.parse returns the same result when you convert the string back to time for both formats. It'd only be a minor inconvenience when searching logs for time stamps, but if you're grepping anyway adding a few \s+ isn't really a big deal.
  • ConorSheehan1
    ConorSheehan1 over 5 years
    @nornagon looks like the issue you saw was ActiveSupport's redefinition of to_json, as mentioned in Ammo Goettsch's comment
  • sekmo
    sekmo over 5 years
    Is there a better way to do something like JSON.pretty_generate(JSON.parse(json_stuff)) if you have a json and not a hash?