760

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?

6
  • 3
    Not sure where you're looking at it, but in webkit's console it creates a nice tree out of any JSON logged or requested. Commented Aug 18, 2009 at 3:59
  • 11
    One thing to remember when doing this, is that your JSON content's size will balloon because of the additional whitespace. In a development environment it is often helpful to have the JSON easy to read, but in a production environment you want your content to be as lean as you can get it for speed and responsiveness in the user's browser. Commented Jun 8, 2011 at 19:39
  • 2
    use y my_json will nicely format stuff if you wanna some quick fix.
    – randomor
    Commented Feb 3, 2013 at 1:28
  • 5
    @randomor undefined method 'y' for main:Object
    – nurettin
    Commented Apr 11, 2013 at 10:36
  • y is available in rails console. Commented Jun 21, 2016 at 23:43

20 Answers 20

1165

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"
}
9
  • 41
    Nifty! I've put this into my ~/.irbrc: def json_pp(json) puts JSON.pretty_generate(JSON.parse(json)) end Commented Nov 22, 2010 at 15:03
  • 12
    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 }
    – iconoclast
    Commented Sep 20, 2011 at 1:25
  • 12
    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. Commented Sep 20, 2011 at 3:37
  • 12
    @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.
    – iconoclast
    Commented Sep 20, 2011 at 15:07
  • 41
    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) }. Commented Sep 21, 2011 at 4:42
113

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 %>
0
82

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)

10
  • 2
    How are you getting around ActiveSupport's redefinition of to_json? This keeps me from pretty printing while ActiveSupport is present. Commented Aug 30, 2013 at 14:33
  • 1
    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.
    – gertas
    Commented Aug 31, 2013 at 23:06
  • 1
    I had to change the parse line to obj = JSON.parse(response.body.first) to make it work. Commented Oct 1, 2013 at 14:28
  • 5
    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.
    – elsurudo
    Commented Jan 20, 2014 at 11:22
  • 3
    In Rails 5 I had to change Rack::Utils.bytesize(pretty_str).to_s to pretty_str.bytesize.to_s and it works great!
    – panteo
    Commented Jul 29, 2016 at 9:28
27

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.

1
  • This one is really good and makes the results easier to read. It shows the results in different colors and is great for Active-record, hash or array... Commented Nov 10, 2022 at 8:35
26

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
4
  • This is awesome, but it actually causes dates/times to render differently: gist.github.com/nornagon/9c24b68bd6d3e871add3
    – nornagon
    Commented Apr 17, 2015 at 3:36
  • 1
    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. Commented Aug 19, 2015 at 5:20
  • @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. Commented Sep 27, 2018 at 16:27
  • @nornagon looks like the issue you saw was ActiveSupport's redefinition of to_json, as mentioned in Ammo Goettsch's comment Commented Oct 1, 2018 at 12:07
19

Using <pre> HTML code and pretty_generate is good trick:

<%
  require 'json'

  hash = JSON[{hey: "test", num: [{one: 1, two: 2, threes: [{three: 3, tthree: 33}]}]}.to_json] 
%>

<pre>
  <%=  JSON.pretty_generate(hash) %>
</pre>
19

If you find that the pretty_generate option built into Ruby's JSON library is not "pretty" enough, I recommend my own NeatJSON gem for your formatting.

To use it:

gem install neatjson

and then use

JSON.neat_generate

instead of

JSON.pretty_generate

Like Ruby's pp it will keep objects and arrays on one line when they fit, but wrap to multiple as needed. For example:

{
  "navigation.createroute.poi":[
    {"text":"Lay in a course to the Hilton","params":{"poi":"Hilton"}},
    {"text":"Take me to the airport","params":{"poi":"airport"}},
    {"text":"Let's go to IHOP","params":{"poi":"IHOP"}},
    {"text":"Show me how to get to The Med","params":{"poi":"The Med"}},
    {"text":"Create a route to Arby's","params":{"poi":"Arby's"}},
    {
      "text":"Go to the Hilton by the Airport",
      "params":{"poi":"Hilton","location":"Airport"}
    },
    {
      "text":"Take me to the Fry's in Fresno",
      "params":{"poi":"Fry's","location":"Fresno"}
    }
  ],
  "navigation.eta":[
    {"text":"When will we get there?"},
    {"text":"When will I arrive?"},
    {"text":"What time will I get to the destination?"},
    {"text":"What time will I reach the destination?"},
    {"text":"What time will it be when I arrive?"}
  ]
}

It also supports a variety of formatting options to further customize your output. For example, how many spaces before/after colons? Before/after commas? Inside the brackets of arrays and objects? Do you want to sort the keys of your object? Do you want the colons to all be lined up?

0
17

Dumping an ActiveRecord object to JSON (in the Rails console):

pp User.first.as_json

# => {
 "id" => 1,
 "first_name" => "Polar",
 "last_name" => "Bear"
}
1
  • 3
    to get a string from pp instead of printing to standard output, use User.first.as_json.pretty_inspect. Works well for me. Commented Dec 15, 2016 at 10:49
9

Here is a middleware solution modified from this excellent answer by @gertas. This solution is not Rails specific--it should work with any Rack application.

The middleware technique used here, using #each, is explained at ASCIIcasts 151: Rack Middleware by Eifion Bedford.

This code goes in app/middleware/pretty_json_response.rb:

class PrettyJsonResponse

  def initialize(app)
    @app = app
  end

  def call(env)
    @status, @headers, @response = @app.call(env)
    [@status, @headers, self]
  end

  def each(&block)
    @response.each do |body|
      if @headers["Content-Type"] =~ /^application\/json/
        body = pretty_print(body)
      end
      block.call(body)
    end
  end

  private

  def pretty_print(json)
    obj = JSON.parse(json)  
    JSON.pretty_unparse(obj)
  end

end

To turn it on, add this to config/environments/test.rb and config/environments/development.rb:

config.middleware.use "PrettyJsonResponse"

As @gertas warns in his version of this solution, avoid using it in production. It's somewhat slow.

Tested with Rails 4.1.6.

8

if you want to handle active_record object, puts is enough.

for example:

  • without puts
2.6.0 (main):0 > User.first.to_json
  User Load (0.4ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
=> "{\"id\":1,\"admin\":true,\"email\":\"[email protected]\",\"password_digest\":\"$2a$10$TQy3P7NT8KrdCzliNUsZzuhmo40LGKoth2hwD3OI.kD0lYiIEwB1y\",\"created_at\":\"2021-07-20T08:34:19.350Z\",\"updated_at\":\"2021-07-20T08:34:19.350Z\",\"name\":\"Arden Stark\"}"
  • with puts
2.6.0 (main):0 > puts User.first.to_json
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
{"id":1,"admin":true,"email":"[email protected]","password_digest":"$2a$10$TQy3P7NT8KrdCzliNUsZzuhmo40LGKoth2hwD3OI.kD0lYiIEwB1y","created_at":"2021-07-20T08:34:19.350Z","updated_at":"2021-07-20T08:34:19.350Z","name":"Arden Stark"}
=> nil

if you are handle the json data, JSON.pretty_generate is a good alternative

Example:

obj = {foo: [:bar, :baz], bat: {bam: 0, bad: 1}}
json = JSON.pretty_generate(obj)
puts json

Output:

{
  "foo": [
    "bar",
    "baz"
  ],
  "bat": {
    "bam": 0,
    "bad": 1
  }
}

if it's in the ROR project, I always prefer to use gem pry-rails to format my codes in the rails console rather than awesome_print which is too verbose.

Example of pry-rails:

enter image description here

it also has syntax highlight.

7
#At Controller
def branch
    @data = Model.all
    render json: JSON.pretty_generate(@data.as_json)
end
6

If you're looking to quickly implement this in a Rails controller action to send a JSON response:

def index
  my_json = '{ "key": "value" }'
  render json: JSON.pretty_generate( JSON.parse my_json )
end
5

Here's my solution which I derived from other posts during my own search.

This allows you to send the pp and jj output to a file as needed.

require "pp"
require "json"

class File
  def pp(*objs)
    objs.each {|obj|
      PP.pp(obj, self)
    }
    objs.size <= 1 ? objs.first : objs
  end
  def jj(*objs)
    objs.each {|obj|
      obj = JSON.parse(obj.to_json)
      self.puts JSON.pretty_generate(obj)
    }
    objs.size <= 1 ? objs.first : objs
  end
end

test_object = { :name => { first: "Christopher", last: "Mullins" }, :grades => [ "English" => "B+", "Algebra" => "A+" ] }

test_json_object = JSON.parse(test_object.to_json)

File.open("log/object_dump.txt", "w") do |file|
  file.pp(test_object)
end

File.open("log/json_dump.txt", "w") do |file|
  file.jj(test_json_object)
end
5

I have used the gem CodeRay and it works pretty well. The format includes colors and it recognises a lot of different formats.

I have used it on a gem that can be used for debugging rails APIs and it works pretty well.

By the way, the gem is named 'api_explorer' (http://www.github.com/toptierlabs/api_explorer)

4

Pretty print variant (Rails):

my_obj = {
  'array' => [1, 2, 3, { "sample" => "hash"}, 44455, 677778, nil ],
  foo: "bar", rrr: {"pid": 63, "state with nil and \"nil\"": false},
  wwww: 'w' * 74
}
require 'pp'
puts my_obj.as_json.pretty_inspect.
            gsub('=>', ': ').
            gsub(/"(?:[^"\\]|\\.)*"|\bnil\b/) {|m| m == 'nil' ? 'null' : m }.
            gsub(/\s+$/, "")

Result:

{"array": [1, 2, 3, {"sample": "hash"}, 44455, 677778, null],
 "foo": "bar",
 "rrr": {"pid": 63, "state with nil and \"nil\"": false},
 "wwww":
  "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww"}

3

# example of use:
a_hash = {user_info: {type: "query_service", e_mail: "[email protected]", phone: "+79876543322"}, cars_makers: ["bmw", "mitsubishi"], car_models: [bmw: {model: "1er", year_mfc: 2006}, mitsubishi: {model: "pajero", year_mfc: 1997}]}
pretty_html = a_hash.pretty_html

# include this module to your libs:
module MyPrettyPrint
    def pretty_html indent = 0
        result = ""
        if self.class == Hash
            self.each do |key, value|
                result += "#{key}

: #{[Array, Hash].include?(value.class) ? value.pretty_html(indent+1) : value}

" end elsif self.class == Array result = "[#{self.join(', ')}]" end "#{result}" end end class Hash include MyPrettyPrint end class Array include MyPrettyPrint end
3

Simplest example, I could think of:

my_json = '{ "name":"John", "age":30, "car":null }'
puts JSON.pretty_generate(JSON.parse(my_json))

Rails console example:

core dev 1555:0> my_json = '{ "name":"John", "age":30, "car":null }'
=> "{ \"name\":\"John\", \"age\":30, \"car\":null }"
core dev 1556:0> puts JSON.pretty_generate(JSON.parse(my_json))
{
  "name": "John",
  "age": 30,
  "car": null
}
=> nil
2

If you are using RABL you can configure it as described here to use JSON.pretty_generate:

class PrettyJson
  def self.dump(object)
    JSON.pretty_generate(object, {:indent => "  "})
  end
end

Rabl.configure do |config|
  ...
  config.json_engine = PrettyJson if Rails.env.development?
  ...
end

A problem with using JSON.pretty_generate is that JSON schema validators will no longer be happy with your datetime strings. You can fix those in your config/initializers/rabl_config.rb with:

ActiveSupport::TimeWithZone.class_eval do
  alias_method :orig_to_s, :to_s
  def to_s(format = :default)
    format == :default ? iso8601 : orig_to_s(format)
  end
end
1

I use the following as I find the headers, status and JSON output useful as a set. The call routine is broken out on recommendation from a railscasts presentation at: http://railscasts.com/episodes/151-rack-middleware?autoplay=true

  class LogJson

  def initialize(app)
    @app = app
  end

  def call(env)
    dup._call(env)
  end

  def _call(env)
    @status, @headers, @response = @app.call(env)
    [@status, @headers, self]
  end

  def each(&block)
    if @headers["Content-Type"] =~ /^application\/json/
      obj = JSON.parse(@response.body)
      pretty_str = JSON.pretty_unparse(obj)
      @headers["Content-Length"] = Rack::Utils.bytesize(pretty_str).to_s
      Rails.logger.info ("HTTP Headers:  #{ @headers } ")
      Rails.logger.info ("HTTP Status:  #{ @status } ")
      Rails.logger.info ("JSON Response:  #{ pretty_str} ")
    end

    @response.each(&block)
  end
  end
1

I had a JSON object in the rails console, and wanted to display it nicely in the console (as opposed to displaying like a massive concatenated string), it was as simple as:

data.as_json
1
  • I arrived back at my own answer (again after trying all the others) 6 months later. The mistake I was making a lot of was using .to_json instead of .as_json.
    – stevec
    Commented Dec 8, 2022 at 12:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.