746

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?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
JP Richardson
  • 38,609
  • 36
  • 119
  • 151
  • 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. – the Tin Man Jun 08 '11 at 19:39
  • 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. – Ryan Florence Aug 18 '09 at 03:59
  • 2
    use `y my_json` will nicely format stuff if you wanna some quick fix. – randomor Feb 03 '13 at 01:28
  • 5
    @randomor `undefined method 'y' for main:Object` – nurettin Apr 11 '13 at 10:36
  • `y` is available in rails console. – Sophia Feng Jun 21 '16 at 23:43
  • `y` prints out YAML formatting (which is always pretty); `j` prints out JSON formatting, but not prettified. [TheDeadSerious's comment](https://stackoverflow.com/questions/86653/how-can-i-pretty-format-my-json-output-in-ruby-on-rails#comment4599397_1823885) shows you how you can get pretty printouts instead. – PJSCopeland Jun 12 '18 at 00:17

20 Answers20

1154

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"
}
Jason Martin
  • 238
  • 2
  • 8
lambshaanxy
  • 22,552
  • 10
  • 68
  • 92
  • 40
    Nifty! I've put this into my ~/.irbrc: def json_pp(json) puts JSON.pretty_generate(JSON.parse(json)) end – TheDeadSerious Nov 22 '10 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 Sep 20 '11 at 01: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. – lambshaanxy Sep 20 '11 at 03: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 Sep 20 '11 at 15:07
  • 40
    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) }`. – lambshaanxy Sep 21 '11 at 04:42
  • 2
    @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))` – Sheharyar Sep 28 '13 at 20:39
  • 1
    If you have to call `JSON.parse` on it, your `my_json` is a string, not JSON! – lambshaanxy Sep 29 '13 at 10:43
  • 2
    There is also handy method in console `jj`, which pretty prints json object to STDOUT – Lev Lukomsky Feb 15 '18 at 23:54
  • 1
    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? – sekmo Dec 10 '18 at 12:57
107

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 %>
Cannon Moyer
  • 3,014
  • 3
  • 31
  • 75
Roger Garza
  • 1,493
  • 1
  • 12
  • 11
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)

gertas
  • 16,869
  • 1
  • 76
  • 58
  • 2
    How are you getting around ActiveSupport's [redefinition](https://github.com/rails/rails/blob/3-0-stable/activesupport/lib/active_support/core_ext/object/to_json.rb) of to_json? This keeps me from pretty printing while ActiveSupport is present. – Ammo Goettsch Aug 30 '13 at 14:33
  • 1
    I don't care really, to_json, as_json, [jbuilder](https://github.com/rails/jbuilder) which I use mostly - whatever, middleware transforms any JSON output. I try to avoid opening classes whenever possible. – gertas Aug 31 '13 at 23:06
  • 1
    I had to change the parse line to `obj = JSON.parse(response.body.first)` to make it work. – Kimmo Lehto Oct 01 '13 at 14:28
  • 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 http://apidock.com/rails/v3.2.13/ActionDispatch/Response/body – gertas Oct 01 '13 at 21:42
  • 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 Jan 20 '14 at 11:22
  • I like this one, and I am looking forward to using it. – Matthew Brown Aug 01 '14 at 14:07
  • See http://stackoverflow.com/a/26491790/238886 for an evolution of this solution that does not call `response.body` and should work in any Rack app. – Wayne Conrad Oct 21 '14 at 16:40
  • Working wonderfully for me with rails 4.2.5 and Puma as my server. Absolutely lovely. Thank you! – counterbeing Mar 08 '16 at 07:23
  • 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 Jul 29 '16 at 09:28
  • Fantastic, thanks! I made a gem out of this: https://github.com/zeiv/rails_pretty_json – Xavier Feb 08 '17 at 20:31
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
Ed Lebert
  • 431
  • 5
  • 5
  • This is awesome, but it actually causes dates/times to render differently: https://gist.github.com/nornagon/9c24b68bd6d3e871add3 – nornagon Apr 17 '15 at 03: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. – Christopher Oezbek Aug 19 '15 at 05: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. – ConorSheehan1 Sep 27 '18 at 16:27
  • @nornagon looks like the issue you saw was ActiveSupport's [redefinition](https://github.com/rails/rails/blob/3-0-stable/activesupport/lib/active_support/core_ext/object/to_json.rb) of to_json, as mentioned in [Ammo Goettsch's comment](https://stackoverflow.com/a/13029297/6305204) – ConorSheehan1 Oct 01 '18 at 12:07
26

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.

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Synthead
  • 2,162
  • 5
  • 22
  • 25
  • 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... – Alireza mohagheghi Nov 10 '22 at 08:35
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?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Phrogz
  • 296,393
  • 112
  • 651
  • 745
17

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

pp User.first.as_json

# => {
 "id" => 1,
 "first_name" => "Polar",
 "last_name" => "Bear"
}
Thomas Klemm
  • 10,678
  • 1
  • 51
  • 54
  • 3
    to get a string from `pp` instead of printing to standard output, use `User.first.as_json.pretty_inspect`. Works well for me. – Johnny Wong Dec 15 '16 at 10:49
17

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>
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
oj5th
  • 1,379
  • 10
  • 25
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.

Community
  • 1
  • 1
Wayne Conrad
  • 103,207
  • 26
  • 155
  • 191
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
sealocal
  • 10,897
  • 3
  • 37
  • 50
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)

Tony
  • 10,088
  • 20
  • 85
  • 139
5

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\":\"admin@gmail.com\",\"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":"admin@gmail.com","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.

TorvaldsDB
  • 766
  • 9
  • 8
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"}

SergA
  • 1,097
  • 13
  • 21
3

# example of use:
a_hash = {user_info: {type: "query_service", e_mail: "my@email.com", 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
Sergio Belevskij
  • 2,478
  • 25
  • 24
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
Martin Carstens
  • 716
  • 8
  • 10
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
Jim Flood
  • 8,144
  • 3
  • 36
  • 48
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
TheDadman
  • 43
  • 4
0

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
stevec
  • 41,291
  • 27
  • 223
  • 311
  • 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 Dec 08 '22 at 12:23