Как «красиво» отформатировать вывод JSON в Ruby on Rails


627

Я хотел бы, чтобы мой вывод JSON в Ruby on Rails был «красивым» или красиво отформатированным.

Прямо сейчас я звоню, to_jsonи мой JSON находится на одной линии. Иногда бывает трудно увидеть, есть ли проблема в потоке вывода JSON.

Есть ли способ настроить, чтобы сделать мой JSON "красивым" или красиво отформатированным в Rails?


2
Не уверен, где вы на это смотрите, но в консоли webkit он создает красивое дерево из любого JSON, зарегистрированного или запрошенного.
Райан Флоренс

8
При этом следует помнить одну вещь: размер содержимого JSON будет увеличиваться из-за дополнительных пробелов. В среде разработки часто бывает полезно иметь JSON легко читаемым, но в производственной среде вы хотите, чтобы ваш контент был настолько же скудным, насколько вы можете получить его для скорости и скорости отклика в браузере пользователя.
Жестянщик

2
Использование y my_jsonбудет хорошо отформатировать материал, если вы хотите быстро исправить.
рандомизировано

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

yдоступен в консоли рельсов.
София Фенг

Ответы:


1000

Используйте pretty_generate()функцию, встроенную в более поздние версии JSON. Например:

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

Который получает вас:

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

32
Острота! Я поместил это в мой ~ / .irbrc: def json_pp (json) помещает конец JSON.pretty_generate (JSON.parse (json))
TheDeadSerious

10
Чтобы сделать это полезным в Rails, кажется, что вы должны дать ответ, включающий код, который можно использовать в том же контексте, что иformat.json { render :json => @whatever }
iconoclast

9
Конечно, prettyprinting следует использовать только для отладки на стороне сервера? Если вы вставите приведенный выше код в контроллер, у вас будет множество бесполезных пробелов во всех ответах, которые даже не нужны для отладки на стороне клиента, так как любые инструменты, достойные их внимания (например, Firebug), уже обрабатывают довольно печатный JSON.
lambshaanxy

8
@jpatokal: вы можете подумать, что есть и другие лучшие варианты, но вопрос был в том, как заставить это работать в Rails. Сказать «вы не хотите делать это в Rails» - это не ответ. Очевидно, что многие люди хотят сделать это в Rails.
иконоборчество

39
В оригинальном постере ничего не сказано о том, где в приложении Rails он хочет это использовать, поэтому я ответил строкой Ruby, которая будет работать где угодно. Для того, чтобы использовать его для создания ответа JSON в Rails контроллера , вы уже ответили на свой вопрос: format.json { render :json => JSON.pretty_generate(my_json) }.
lambshaanxy

78

Благодаря Rack Middleware и Rails 3 вы можете выводить симпатичные JSON для каждого запроса, не меняя контроллер вашего приложения. Я написал такой фрагмент промежуточного программного обеспечения, и я получил хорошо напечатанный JSON в браузере и curlвывод.

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

Приведенный выше код должен быть размещен в app/middleware/pretty_json_response.rbвашем Rails-проекте. И последний шаг - регистрация промежуточного программного обеспечения в config/environments/development.rb:

config.middleware.use PrettyJsonResponse

Я не рекомендую использовать его вproduction.rb . Повторная обработка JSON может ухудшить время отклика и производительность вашего рабочего приложения. Со временем может быть введена дополнительная логика, такая как заголовок «X-Pretty-Json: true», чтобы инициировать форматирование для запросов скручивания вручную по требованию.

(Протестировано с Rails 3.2.8-5.0.0, Ruby 1.9.3-2.2.0, Linux)


2
Как вы справляетесь с переопределением ActiveSupport to_json? Это мешает мне красиво печатать, пока присутствует ActiveSupport.
Боеприпасы Goettsch

1
Мне все равно, to_json, as_json, jbuilder, который я использую в основном - что угодно, промежуточное программное обеспечение преобразует любой вывод JSON. Я стараюсь по возможности избегать открытия классов.
Гертас

1
Мне пришлось изменить строку разбора, obj = JSON.parse(response.body.first)чтобы заставить ее работать.
Киммо Лехто

5
Отлично работает и в Rails 4 ... спасибо! Я предпочитаю это более специфичным для библиотеки методам (как в принятом ответе). Так как в любом случае вы должны использовать это только в режиме разработки, снижение производительности не имеет большого значения.
Эльсурудо

3
В Rails 5 мне пришлось поменять Rack::Utils.bytesize(pretty_str).to_sна pretty_str.bytesize.to_sи это прекрасно работает!
Пантео

78

<pre>Тег в HTML, используемый с JSON.pretty_generate, будет оказывать JSON довольно с вашей точки зрения. Я был так счастлив, когда мой выдающийся босс показал мне это:

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

5
Так чисто и лаконично!
Шон Сзурко

23

Если ты хочешь:

  1. Уточните все исходящие ответы JSON из вашего приложения автоматически.
  2. Избегайте загрязнения объекта # to_json / # as_json
  3. Избегайте анализа / повторного рендеринга JSON с использованием промежуточного программного обеспечения (YUCK!)
  4. Сделай это ЖЕЛЕЗНЫМ ПУТОМ!

Затем ... замените ActionController :: Renderer на JSON! Просто добавьте следующий код в ваш 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

Это потрясающе, но на самом деле это приводит к тому, что даты и время отображаются по-разному: gist.github.com/nornagon/9c24b68bd6d3e871add3
nornagon

Несколько проблем с этим: (1) JSON.pretty_generate требует json.respond_to?(:to_h)или :to_hash. (2) pretty_generate может подавиться вещами, которых нет в to_json.
Кристофер Оезбек

@nornagon Я не применил это изменение, и я получаю ту же разницу, которую вы видели между .to_json и pretty_generate. Я вижу это только в консоли рельсов, а не просто в IRB. Я думаю, что это может быть обычным делом, ничего общего с этим патчем. Кроме того, Time.parse возвращает тот же результат, когда вы переводите строку обратно во время для обоих форматов. Это будет только незначительное неудобство при поиске в журналах меток времени, но если вы все равно будете добавлять, добавьте несколько \ s +, это не так уж и важно.
con--

@nornagon похоже, что проблема, которую вы видели, была переопределением ActiveSupport to_json, как упомянуто в комментарии Ammo Goettsch
con--

17

Проверьте Awesome Print . Разобрать строку JSON в Ruby Hash, а затем отобразить ее apтак:

require "awesome_print"
require "json"

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

ap(JSON.parse(json))

С учетом вышесказанного вы увидите:

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

Awesome Print также добавит цвет, который не будет отображаться при переполнении стека.


2
Согласен с тобой! awesome_print это просто потрясающе!
Аашиш

2
Мы также используем awesome_print для наших проектов, и он работает так, как его называют -> awesome
Simon Franzen

13

Выгрузка объекта ActiveRecord в JSON (в консоли Rails):

pp User.first.as_json

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

3
чтобы получить строку ppвместо печати в стандартный вывод, используйте User.first.as_json.pretty_inspect. Хорошо работает для меня.
Джонни Вонг

12

Использование <pre>HTML-кода и pretty_generateхороший трюк:

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

Если вы обнаружите, что pretty_generateопция, встроенная в библиотеку JSON в Ruby, недостаточно «хороша», я рекомендую для вашего форматирования использовать мой собственный гем NeatJSON .

Чтобы использовать это:

gem install neatjson

а затем использовать

JSON.neat_generate

вместо

JSON.pretty_generate

Как и в Ruby, ppон будет держать объекты и массивы на одной строке, когда они подходят, но при необходимости переносить их на несколько. Например:

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

Он также поддерживает различные параметры форматирования для дальнейшей настройки вашего вывода. Например, сколько пробелов до / после двоеточия? До / после запятых? Внутри скобок массивов и объектов? Вы хотите отсортировать ключи вашего объекта? Вы хотите, чтобы все колоны были выстроены в очередь?


2
Этот драгоценный камень скалывает - выравнивание по колонам особенно сладкое!
webdevguy

8

Вот промежуточное решение, измененное из этого превосходного ответа @gertas . Это решение не является специфичным для Rails - оно должно работать с любым Rack-приложением.

Используемая здесь технология промежуточного программного обеспечения с использованием #each объясняется в ASCIIcasts 151: промежуточное программное обеспечение стойки Эфионом Бедфордом.

Этот код идет в 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

Чтобы включить его, добавьте его в config / environment / test.rb и config / environment / development.rb:

config.middleware.use "PrettyJsonResponse"

Как предупреждает @gertas в своей версии этого решения, избегайте его использования в производстве. Это несколько медленно.

Протестировано с Rails 4.1.6.



4

Вот мое решение, которое я извлек из других постов во время моего собственного поиска.

Это позволяет отправлять вывод pp и jj в файл по мере необходимости.

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

Я использовал гем CodeRay, и он работает довольно хорошо. Формат включает в себя цвета, и он распознает много разных форматов.

Я использовал его на геме, который можно использовать для отладки API рельсов, и он работает довольно хорошо.

Кстати, этот самоцвет называется api_explorer ( http://www.github.com/toptierlabs/api_explorer )


3

Если вы хотите быстро реализовать это в действии контроллера Rails для отправки ответа JSON:

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

2

Если вы используете RABL, вы можете настроить его, как описано здесь, для использования 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

Проблема с использованием JSON.pretty_generate заключается в том, что валидаторы схемы JSON больше не будут удовлетворены вашими строками даты и времени. Вы можете исправить это в вашем config / initializers / rabl_config.rb с помощью:

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

Я использую следующее, поскольку нахожу заголовки, статус и вывод JSON полезными в виде набора. Процедура вызова нарушена по рекомендации из презентации Railscasts по адресу: 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

Довольно печатный вариант:

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

Результат:

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

0

Простейший пример, который я мог бы представить:

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

Пример консоли 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.