Cách thức đầu ra JSON khá định dạng JSON trong Ruby on Rails


626

Tôi muốn đầu ra JSON của tôi trong Ruby on Rails là "đẹp" hoặc được định dạng độc đáo.

Ngay bây giờ, tôi gọi to_jsonvà JSON của tôi là tất cả trên một dòng. Đôi khi điều này có thể khó thấy nếu có vấn đề trong luồng đầu ra JSON.

Có cách nào để định cấu hình để làm cho JSON của tôi "đẹp" hoặc được định dạng độc đáo trong Rails không?


2
Không chắc chắn nơi bạn đang xem nó, nhưng trong bảng điều khiển của webkit, nó tạo ra một cây đẹp từ bất kỳ JSON nào được ghi lại hoặc yêu cầu.
Ryan Florence

8
Một điều cần nhớ khi thực hiện điều này là kích thước của nội dung JSON của bạn sẽ tăng lên do khoảng trắng bổ sung. Trong môi trường phát triển, việc đọc JSON dễ đọc, nhưng trong môi trường sản xuất, bạn muốn nội dung của mình gọn gàng như bạn có thể có được tốc độ và khả năng phản hồi trong trình duyệt của người dùng.
Tin Man

2
sử dụng y my_jsonsẽ định dạng độc đáo công cụ nếu bạn muốn một số sửa chữa nhanh chóng.
ngẫu nhiên

5
@randomorundefined method 'y' for main:Object
Nurettin

ycó sẵn trong bảng điều khiển đường ray.
Sophia Feng

Câu trả lời:


999

Sử dụng pretty_generate()hàm, được tích hợp vào các phiên bản sau của JSON. Ví dụ:

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

Điều này giúp bạn:

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

32
Tiện lợi! Tôi đã đặt cái này vào ~ / .irbrc: def json_pp (json) đặt JSON.pretty_generate (JSON.parse (json))
TheDeadSerious

10
Để làm cho điều này hữu ích trong Rails, có vẻ như bạn nên đưa ra câu trả lời bao gồm mã có thể được sử dụng trong cùng bối cảnh vớiformat.json { render :json => @whatever }
iconoclast

9
Chắc chắn việc in đẹp chỉ nên được sử dụng để gỡ lỗi phía máy chủ? Nếu bạn dán mã ở trên trong bộ điều khiển, bạn sẽ có vô số khoảng trắng vô dụng trong tất cả các phản hồi, thậm chí không cần thiết cho việc gỡ lỗi phía máy khách vì bất kỳ công cụ nào có giá trị muối của chúng (ví dụ: Fireorms) đã xử lý JSON in đẹp.
lambshaanxy

8
@jpatokal: bạn có thể cân nhắc có những lựa chọn khác tốt hơn, nhưng câu hỏi là làm thế nào để nó hoạt động trong Rails. Nói "bạn không muốn làm điều đó trong Rails" là một câu trả lời không. Rõ ràng rất nhiều người muốn làm điều đó trong Rails.
iconoclast

39
Những poster ban đầu không nói gì về nơi trong một ứng dụng Rails ông muốn sử dụng này, vì vậy tôi đã trả lời với một dòng của Ruby sẽ làm việc bất cứ nơi nào. Để sử dụng nó để tạo phản hồi JSON trong bộ điều khiển Rails , bạn đã trả lời câu hỏi của riêng mình : format.json { render :json => JSON.pretty_generate(my_json) }.
lambshaanxy

78

Nhờ Rack Middleware và Rails 3, bạn có thể xuất JSON đẹp cho mọi yêu cầu mà không cần thay đổi bất kỳ bộ điều khiển nào của ứng dụng. Tôi đã viết đoạn mã trung gian như vậy và tôi nhận được JSON được in độc đáo trong trình duyệt và curlđầu ra.

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

Mã ở trên nên được đặt trong app/middleware/pretty_json_response.rbdự án Rails của bạn. Và bước cuối cùng là đăng ký phần mềm trung gian trong config/environments/development.rb:

config.middleware.use PrettyJsonResponse

Tôi không khuyên bạn nên sử dụng nó trongproduction.rb . Việc lặp lại JSON có thể làm giảm thời gian phản hồi và thông lượng của ứng dụng sản xuất của bạn. Cuối cùng, logic bổ sung, chẳng hạn như tiêu đề 'X-Pretty-Json: true' có thể được giới thiệu để kích hoạt định dạng cho các yêu cầu cuộn thủ công theo yêu cầu.

(Đã thử nghiệm với Rails 3.2.8-5.0.0, Ruby 1.9.3-2.2.0, Linux)


2
Làm thế nào bạn nhận được xung quanh việc xác định lại to_json của ActiveSupport? Điều này giúp tôi không bị in ấn trong khi ActiveSupport có mặt.
Ammo Goettsch

1
Tôi thực sự không quan tâm, to_json, as_json, jbuilder mà tôi sử dụng chủ yếu - bất cứ thứ gì, phần mềm trung gian đều biến đổi bất kỳ đầu ra JSON nào. Tôi cố gắng tránh mở lớp bất cứ khi nào có thể.
gertas

1
Tôi đã phải thay đổi dòng phân tích obj = JSON.parse(response.body.first)để làm cho nó hoạt động.
Kimmo Lehto

5
Hoạt động tuyệt vời trong Rails 4 cũng ... cảm ơn! Tôi thích điều này hơn các phương pháp dành riêng cho thư viện (như trong câu trả lời được chấp nhận). Vì dù sao bạn chỉ nên sử dụng điều này trong chế độ dev, nên hiệu năng đạt được không phải là vấn đề lớn.
elsurudo

3
Trong Rails 5 tôi đã phải thay đổi Rack::Utils.bytesize(pretty_str).to_sđể pretty_str.bytesize.to_svà nó hoạt động tuyệt vời!
panteo

77

Các <pre>thẻ trong HTML, sử dụng với JSON.pretty_generate, sẽ làm cho JSON khá theo quan điểm của bạn. Tôi đã rất hạnh phúc khi ông chủ lừng lẫy của tôi chỉ cho tôi điều này:

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

5
Thật sạch sẽ và súc tích!
Sean Szurko

23

Nếu bạn muốn:

  1. Tự động hoàn thành tất cả các phản hồi JSON từ ứng dụng của bạn.
  2. Tránh gây ô nhiễm đối tượng # to_json / # as_json
  3. Tránh phân tích cú pháp / kết xuất lại JSON bằng cách sử dụng phần mềm trung gian (Yucks!)
  4. Làm điều đó CÁCH RA MẮT!

Sau đó ... thay thế ActionControll :: Renderer cho JSON! Chỉ cần thêm đoạn mã sau vào ApplicationContoder của bạn:

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

Điều này thật tuyệt vời, nhưng nó thực sự khiến ngày / thời gian hiển thị khác nhau: gist.github.com/nornagon/9c24b68bd6d3e871add3
nornagon

Một số vấn đề với điều này: (1) JSON.pretty_generate yêu cầu json.respond_to?(:to_h)hoặc :to_hash. (2) beautiful_generate có thể gây nghẹt thở cho những thứ mà to_json không có.
Christopher Oezbek

@nornagon Tôi chưa áp dụng thay đổi này và tôi nhận được sự khác biệt giống như bạn đã thấy giữa .to_json và beautiful_generate. Tôi chỉ nhìn thấy nó trong một bảng điều khiển đường ray, không phải irb đơn giản. Tôi nghĩ rằng đây có thể là một điều rails chung, không có gì để làm với bản vá này. Ngoài ra, Time.parse trả về cùng một kết quả khi bạn chuyển đổi chuỗi trở lại thời gian cho cả hai định dạng. Nó chỉ là một bất tiện nhỏ khi tìm kiếm nhật ký cho dấu thời gian, nhưng nếu bạn vẫn đang sử dụng một vài \ s + thì thực sự không phải là vấn đề lớn.
con--

@nornagon có vẻ như vấn đề bạn thấy là định nghĩa lại của to_json của ActiveSupport, như được đề cập trong bình luận của Ammo Goettsch
con--

17

Kiểm tra bản in tuyệt vời . Phân tích chuỗi JSON thành Ruby Hash, sau đó hiển thị chuỗi apnhư vậy:

require "awesome_print"
require "json"

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

ap(JSON.parse(json))

Với những điều trên, bạn sẽ thấy:

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

Awesome Print cũng sẽ thêm một số màu mà Stack Overflow sẽ không hiển thị cho bạn.


2
Đồng ý với bạn! awesome_print là đơn giản tuyệt vời!
Aashish

2
Chúng tôi cũng đang sử dụng awesome_print cho các dự án của mình và nó hoạt động như tên là -> tuyệt vời
Simon Franzen

13

Kết xuất một đối tượng ActiveRecord vào JSON (trong bảng điều khiển Rails):

pp User.first.as_json

# => {
 "id" => 1,
 "first_name" => "Polar",
 "last_name" => "Bear"
}

3
để có được một chuỗi từ ppthay vì in ra đầu ra tiêu chuẩn, hãy sử dụng User.first.as_json.pretty_inspect. Hoạt động tốt cho tôi.
Johnny Wong

12

Sử dụng <pre>mã HTML và pretty_generatelà một mẹo hay:

<%
  require 'json'

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

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

12

Nếu bạn thấy rằng pretty_generatetùy chọn được tích hợp trong thư viện JSON của Ruby không đủ "đẹp", tôi khuyên bạn nên sử dụng đá quý NeatJSON của riêng tôi cho định dạng của bạn.

Để dùng nó:

gem install neatjson

và sau đó sử dụng

JSON.neat_generate

thay vì

JSON.pretty_generate

Giống như Ruby, ppnó sẽ giữ các đối tượng và mảng trên một dòng khi chúng khớp, nhưng bọc thành nhiều khi cần thiết. Ví dụ:

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

Nó cũng hỗ trợ nhiều tùy chọn định dạng để tùy chỉnh thêm đầu ra của bạn. Ví dụ, có bao nhiêu khoảng trắng trước / sau dấu hai chấm? Trước / sau dấu phẩy? Bên trong dấu ngoặc của mảng và đối tượng? Bạn có muốn sắp xếp các khóa của đối tượng của bạn? Bạn có muốn tất cả các dấu hai chấm được xếp hàng?


2
Đá quý này - sự liên kết trên dấu hai chấm đặc biệt ngọt ngào!
webdevguy

8

Đây là một giải pháp phần mềm trung gian được sửa đổi từ câu trả lời tuyệt vời này của @gertas . Giải pháp này không phải là Rails cụ thể - nó sẽ hoạt động với bất kỳ ứng dụng Rack nào.

Kỹ thuật phần mềm trung gian được sử dụng ở đây, sử dụng #each, được giải thích tại ASCIIcasts 151: Rack Middleware của Eifion Bedford.

Mã này có trong app / middleware / beautiful_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

Để bật nó lên, hãy thêm cái này vào config / môi trường / test.rb và config / môi trường / Development.rb:

config.middleware.use "PrettyJsonResponse"

Như @gertas cảnh báo trong phiên bản giải pháp này, hãy tránh sử dụng nó trong sản xuất. Nó hơi chậm.

Đã thử nghiệm với Rails 4.1.6.


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

4

Đây là giải pháp của tôi mà tôi bắt nguồn từ các bài đăng khác trong quá trình tìm kiếm của riêng tôi.

Điều này cho phép bạn gửi đầu ra pp và jj đến một tệp khi cần thiết.

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

3

Tôi đã sử dụng GemRay gem và nó hoạt động khá tốt. Các định dạng bao gồm màu sắc và nó nhận ra rất nhiều định dạng khác nhau.

Tôi đã sử dụng nó trên một loại đá quý có thể được sử dụng để gỡ lỗi API đường ray và nó hoạt động khá tốt.

Nhân tiện, viên ngọc được đặt tên là 'api_explorer' ( http://www.github.com/toptierlabs/api_explorer )


3

Nếu bạn đang tìm cách nhanh chóng thực hiện điều này trong hành động của bộ điều khiển Rails để gửi phản hồi JSON:

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

2

Nếu bạn đang sử dụng RABL, bạn có thể định cấu hình nó như được mô tả ở đây để sử dụng 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

Một vấn đề với việc sử dụng JSON.pretty_generate là các trình xác thực lược đồ JSON sẽ không còn hài lòng với các chuỗi thời gian của bạn. Bạn có thể sửa những thứ đó trong config / khởi tạo / rabl_config.rb của bạn bằng:

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

2

# 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

1

Tôi sử dụng như sau khi tôi thấy các tiêu đề, trạng thái và đầu ra JSON hữu ích như một bộ. Thói quen cuộc gọi được chia ra theo khuyến nghị từ bản trình bày railscasts tại: 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

Biến thể in đẹp:

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('=>', ': ')

Kết quả:

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

0

Ví dụ đơn giản nhất, tôi có thể nghĩ về:

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

Ví dụ bảng điều khiển Rails:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.