Cara "cantik" memformat keluaran JSON di Ruby on Rails

626

Saya ingin output JSON saya di Ruby on Rails menjadi "cantik" atau diformat dengan baik.

Saat ini, saya menelepon to_jsondan JSON saya semuanya dalam satu saluran. Kadang-kadang ini bisa sulit untuk melihat apakah ada masalah dalam aliran output JSON.

Apakah ada cara untuk mengkonfigurasi agar JSON saya "cantik" atau diformat dengan baik di Rails?

JP Richardson
sumber
2
Tidak yakin di mana Anda melihatnya, tetapi di konsol webkit itu membuat pohon bagus dari JSON apa pun yang dicatat atau diminta.
Ryan Florence
8
Satu hal yang perlu diingat ketika melakukan ini, adalah ukuran konten JSON Anda akan menggelembung karena spasi tambahan. Dalam lingkungan pengembangan, JSON seringkali mudah dibaca, tetapi dalam lingkungan produksi Anda ingin konten Anda ramping karena Anda dapat memperolehnya dengan cepat dan responsif di peramban pengguna.
the Tin Man
2
Penggunaan y my_jsondengan baik akan memformat barang jika Anda ingin perbaikan cepat.
Randand
5
@randomorundefined method 'y' for main:Object
nurettin
ytersedia di konsol rel.
Sophia Feng

Jawaban:

999

Gunakan pretty_generate()fungsi, yang dibangun ke dalam versi JSON yang lebih baru. Sebagai contoh:

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

Yang membuat Anda:

{
  "array": [
    1,
    2,
    3,
    {
      "sample": "hash"
    }
  ],
  "foo": "bar"
}
lambshaanxy
sumber
32
Bagus! Saya telah memasukkan ini ke ~ / .irbrc saya: def json_pp (json) menempatkan JSON.pretty_generate (JSON.parse (json)) akhir
TheDeadSerious
10
Untuk menjadikan ini berguna dalam Rails, tampaknya Anda harus memberikan jawaban yang mencakup kode yang dapat digunakan dalam konteks yang sama denganformat.json { render :json => @whatever }
iconoclast
9
Tentunya prettyprinting seharusnya hanya digunakan untuk debugging sisi server? Jika Anda menempelkan kode di atas di controller, Anda akan memiliki banyak spasi kosong yang tidak berguna di semua respons, yang bahkan tidak diperlukan untuk debugging sisi-klien karena alat apa pun yang bernilai garam (mis. Firebug) sudah menangani pracyprinting JSON.
lambshaanxy
8
@ jpatokal: Anda mungkin menganggap ada opsi lain yang lebih baik, tetapi pertanyaannya adalah bagaimana membuatnya bekerja di Rails. Mengatakan "Anda tidak ingin melakukan itu di Rails" bukanlah jawaban. Jelas banyak orang ingin melakukannya di Rails.
iconoclast
39
Poster asli tidak mengatakan tentang di mana di aplikasi Rails ia ingin menggunakan ini, jadi saya menjawab dengan garis Ruby yang akan bekerja di mana saja. Untuk menggunakannya untuk menghasilkan respon JSON dalam Rails kontroler , Anda sudah menjawab pertanyaan Anda sendiri: format.json { render :json => JSON.pretty_generate(my_json) }.
lambshaanxy
78

Berkat Rack Middleware dan Rails 3 Anda dapat menampilkan JSON cantik untuk setiap permintaan tanpa mengubah pengontrol aplikasi Anda. Saya telah menulis snipet middleware dan saya mendapatkan JSON yang dicetak dengan baik di browser dan curloutput.

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

Kode di atas harus ditempatkan di app/middleware/pretty_json_response.rbproyek Rails Anda. Dan langkah terakhir adalah mendaftarkan middleware di config/environments/development.rb:

config.middleware.use PrettyJsonResponse

Saya tidak merekomendasikan untuk menggunakannyaproduction.rb . Pemrosesan ulang JSON dapat menurunkan waktu respons dan throughput aplikasi produksi Anda. Akhirnya, logika ekstra seperti tajuk 'X-Pretty-Json: true' dapat diperkenalkan untuk memicu pemformatan untuk permintaan ikal manual sesuai permintaan.

(Diuji dengan Rails 3.2.8-5.0.0, Ruby 1.9.3-2.2.0, Linux)

gertas
sumber
2
Bagaimana Anda menyiasati redefinisi ActiveSupport tentang to_json? Ini membuat saya tidak bisa mencetak dengan baik ketika ActiveSupport hadir.
Ammo Goettsch
1
Saya tidak peduli benar-benar, to_json, as_json, JBuilder yang saya gunakan sebagian besar - apa pun, middleware transformasi setiap JSON output. Saya mencoba untuk menghindari membuka kelas kapan pun memungkinkan.
gertas
1
Saya harus mengubah baris parse obj = JSON.parse(response.body.first)untuk membuatnya bekerja.
Kimmo Lehto
5
Berfungsi bagus di Rails 4 juga ... terima kasih! Saya lebih suka ini daripada metode yang lebih spesifik perpustakaan (seperti dalam jawaban yang diterima). Karena Anda hanya boleh menggunakan ini dalam mode dev, kinerja tidak menjadi masalah besar.
elsurudo
3
Di Rails 5 saya harus beralih Rack::Utils.bytesize(pretty_str).to_ske pretty_str.bytesize.to_sdan itu berhasil!
panteo
78

The <pre>tag di HTML, digunakan dengan JSON.pretty_generate, akan membuat JSON cukup dalam pandangan Anda. Saya sangat senang ketika bos terkenal saya menunjukkan ini kepada saya:

<% if @data.present? %>
   <pre><%= JSON.pretty_generate(@data) %></pre>
<% end %>
Roger Garza
sumber
5
Sangat bersih dan ringkas!
Sean Szurko
23

Jika Anda menghendaki:

  1. Prettify semua respons JSON keluar dari aplikasi Anda secara otomatis.
  2. Hindari mencemari Objek # to_json / # as_json
  3. Hindari parsing / rendering ulang JSON menggunakan middleware (YUCK!)
  4. Lakukan CARA KERETA API!

Lalu ... ganti ActionController :: Renderer untuk JSON! Cukup tambahkan kode berikut ke ApplicationController Anda:

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
sumber
Ini luar biasa, tetapi sebenarnya menyebabkan tanggal / waktu dibuat berbeda: gist.github.com/nornagon/9c24b68bd6d3e871add3
nornagon
Beberapa masalah dengan ini: (1) JSON.pretty_generate memerlukan json.respond_to?(:to_h)atau :to_hash. (2) pretty_generate dapat tersedak hal-hal yang to_json tidak.
Christopher Oezbek
@nagonagon Saya belum menerapkan perubahan ini dan saya mendapatkan perbedaan yang sama seperti yang Anda lihat antara .to_json dan pretty_generate. Saya hanya melihatnya di konsol rel, bukan irb polos. Saya pikir ini bisa menjadi masalah rel umum, tidak ada hubungannya dengan tambalan ini. Time.parse juga mengembalikan hasil yang sama ketika Anda mengubah string kembali ke waktu untuk kedua format. Ini hanya akan menjadi sedikit ketidaknyamanan saat mencari log untuk prangko waktu, tetapi jika Anda tetap ingin menambahkan beberapa \ s + sebenarnya bukan masalah besar.
con--
@nagonagon sepertinya masalah yang Anda lihat adalah definisi ulang ActiveSupport dari to_json, seperti yang disebutkan dalam komentar Ammo Goettsch
con
17

Lihat Cetak Luar Biasa . Parsing string JSON ke Ruby Hash, lalu tampilkan dengan apseperti:

require "awesome_print"
require "json"

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

ap(JSON.parse(json))

Dengan yang di atas, Anda akan melihat:

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

Cetak Keren juga akan menambahkan beberapa warna yang tidak akan ditampilkan oleh Stack Overflow.

Sintead
sumber
2
Setuju dengan Anda! awesome_print sederhana luar biasa!
Aashish
2
Kami menggunakan awesome_print juga untuk proyek kami dan berfungsi seperti namanya -> luar biasa
Simon Franzen
13

Membuang objek ActiveRecord ke JSON (di konsol Rails):

pp User.first.as_json

# => {
 "id" => 1,
 "first_name" => "Polar",
 "last_name" => "Bear"
}
Thomas Klemm
sumber
3
untuk mendapatkan string dari ppalih-alih mencetak ke output standar, gunakan User.first.as_json.pretty_inspect. Bekerja dengan baik untuk saya.
Johnny Wong
12

Menggunakan <pre>kode HTML dan pretty_generatetrik yang bagus:

<%
  require 'json'

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

<pre>
  <%=  JSON.pretty_generate(hash) %>
</pre>
oj5th
sumber
12

Jika Anda menemukan bahwa pretty_generateopsi yang dibangun di perpustakaan JSON Ruby tidak cukup "cantik", saya sarankan permata NeatJSON saya sendiri untuk pemformatan Anda.

Untuk menggunakannya:

gem install neatjson

dan kemudian gunakan

JSON.neat_generate

dari pada

JSON.pretty_generate

Seperti Ruby, ppia akan menyimpan objek dan array pada satu baris saat cocok, tetapi bungkus menjadi beberapa sesuai kebutuhan. Sebagai contoh:

{
  "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?"}
  ]
}

Ini juga mendukung berbagai opsi pemformatan untuk lebih lanjut menyesuaikan output Anda. Misalnya, berapa banyak spasi sebelum / sesudah titik dua? Sebelum / sesudah koma? Di dalam kurung array dan benda? Apakah Anda ingin menyortir kunci objek Anda? Apakah Anda ingin semua titik dua berbaris?

Phrogz
sumber
2
Batu permata ini - garis pada titik dua sangat manis!
webdevguy
8

Ini adalah solusi middleware yang dimodifikasi dari jawaban yang sangat bagus ini oleh @gertas . Solusi ini tidak spesifik untuk Rails - ini harus bekerja dengan aplikasi Rack apa pun.

Teknik middleware yang digunakan di sini, menggunakan #each, dijelaskan di ASCIIcasts 151: Rack Middleware oleh Eifion Bedford.

Kode ini berlaku di 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

Untuk mengaktifkannya, tambahkan ini ke config / environment / test.rb dan config / environment / development.rb:

config.middleware.use "PrettyJsonResponse"

Ketika @gertas memperingatkan dalam versinya tentang solusi ini, hindari menggunakannya dalam produksi. Agak lambat.

Diuji dengan Rails 4.1.6.

Wayne Conrad
sumber
5
#At Controller
def branch
    @data = Model.all
    render json: JSON.pretty_generate(@data.as_json)
end
Буянбат Чойжилсүрэн
sumber
4

Inilah solusi saya yang saya dapatkan dari posting lain selama pencarian saya sendiri.

Ini memungkinkan Anda untuk mengirim output pp dan jj ke file sesuai kebutuhan.

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
Christopher Mullins
sumber
3

Saya telah menggunakan permata CodeRay dan itu bekerja dengan cukup baik. Formatnya termasuk warna dan mengenali banyak format yang berbeda.

Saya telah menggunakannya pada permata yang dapat digunakan untuk debugging rails APIs dan berfungsi dengan baik.

Omong-omong, permata itu bernama 'api_explorer' ( http://www.github.com/toptierlabs/api_explorer )

Tony
sumber
3

Jika Anda ingin menerapkan ini dengan cepat dalam tindakan pengontrol Rails untuk mengirim respons JSON:

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

Jika Anda menggunakan RABL, Anda dapat mengonfigurasinya seperti dijelaskan di sini untuk menggunakan 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

Masalah dengan menggunakan JSON.pretty_generate adalah bahwa validator skema JSON tidak akan lagi senang dengan string datetime Anda. Anda dapat memperbaikinya di config / initializers / rabl_config.rb Anda dengan:

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
sumber
2

# 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
Sergio Belevskij
sumber
1

Saya menggunakan yang berikut ini karena saya menemukan header, status dan output JSON berguna sebagai satu set. Rutinitas panggilan dipecah berdasarkan rekomendasi dari presentasi railscasts di: 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
Ayah
sumber
1

Varian cetak cantik:

my_object = { :array => [1, 2, 3, { :sample => "hash"}, 44455, 677778, 9900 ], :foo => "bar", rrr: {"pid": 63, "state": false}}
puts my_object.as_json.pretty_inspect.gsub('=>', ': ')

Hasil:

{"array": [1, 2, 3, {"sample": "hash"}, 44455, 677778, 9900],
 "foo": "bar",
 "rrr": {"pid": 63, "state": false}}
SergA
sumber
0

Contoh paling sederhana, saya bisa memikirkan:

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

Contoh konsol rel:

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
sumber