Cómo dar formato "bonito" a la salida JSON en Ruby on Rails


626

Me gustaría que mi salida JSON en Ruby on Rails sea "bonita" o bien formateada.

En este momento, llamo to_jsony mi JSON está en una sola línea. A veces esto puede ser difícil de ver si hay un problema en la secuencia de salida JSON.

¿Hay alguna forma de configurar para hacer que mi JSON sea "bonito" o bien formateado en Rails?


2
No estoy seguro de dónde lo está mirando, pero en la consola de webkit crea un buen árbol de cualquier JSON registrado o solicitado.
Ryan Florence

8
Una cosa para recordar al hacer esto es que el tamaño de su contenido JSON aumentará debido al espacio en blanco adicional. En un entorno de desarrollo, a menudo es útil tener el JSON fácil de leer, pero en un entorno de producción desea que su contenido sea lo más ágil posible para obtener velocidad y capacidad de respuesta en el navegador del usuario.
The Tin Man

2
el uso y my_jsonformateará muy bien las cosas si quieres una solución rápida.
azar

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

yEstá disponible en rieles de consola.
Sophia Feng

Respuestas:


999

Use la pretty_generate()función, integrada en versiones posteriores de JSON. Por ejemplo:

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

Lo que te lleva a:

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

32
¡Hábil! He puesto esto en mi ~ / .irbrc: def json_pp (json) pone JSON.pretty_generate (JSON.parse (json)) final
TheDeadSerious

10
Para que esto sea útil en Rails, parece que debería dar una respuesta que incluya código que pueda usarse en el mismo contexto queformat.json { render :json => @whatever }
iconoclasta

99
¿Seguramente la impresión bonita solo debe usarse para la depuración del lado del servidor? Si pega el código anterior en un controlador, tendrá toneladas de espacios en blanco inútiles en todas las respuestas, lo que ni siquiera es necesario para la depuración del lado del cliente, ya que cualquier herramienta que valga la pena (por ejemplo, Firebug) ya maneja JSON de impresión bonita.
lambshaanxy

8
@jpatokal: puede considerar que hay otras opciones mejores, pero la pregunta era cómo hacer que esto funcione en Rails. Decir "no quieres hacer eso en Rails" no es una respuesta. Obviamente, mucha gente quiere hacerlo en Rails.
iconoclasta

39
El póster original no decía nada sobre en qué parte de una aplicación Rails quiere usar esto, así que respondí con una línea de Ruby que funcionaría en cualquier lugar. Para utilizarlo para generar la respuesta JSON en un Rieles controlador , que ya responde a su propia pregunta: format.json { render :json => JSON.pretty_generate(my_json) }.
lambshaanxy

78

Gracias a Rack Middleware y Rails 3, puede generar JSON bonitos para cada solicitud sin cambiar ningún controlador de su aplicación. He escrito tal fragmento de middleware y obtengo JSON muy bien impreso en el navegador y la curlsalida.

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

El código anterior se debe colocar en app/middleware/pretty_json_response.rbsu proyecto Rails. Y el paso final es registrar el middleware en config/environments/development.rb:

config.middleware.use PrettyJsonResponse

No recomiendo usarloproduction.rb . El análisis JSON puede degradar el tiempo de respuesta y el rendimiento de su aplicación de producción. Eventualmente, se puede introducir una lógica adicional como el encabezado 'X-Pretty-Json: true' para activar el formateo de las solicitudes de rizo manual a pedido.

(Probado con Rails 3.2.8-5.0.0, Ruby 1.9.3-2.2.0, Linux)


2
¿Cómo está evitando la redefinición de ActiveSupport de to_json? Esto me impide imprimir bien mientras ActiveSupport está presente.
Munición Goettsch

1
Realmente no me importa, to_json, as_json, jbuilder que uso principalmente, lo que sea, el middleware transforma cualquier salida JSON. Intento evitar abrir clases siempre que sea posible.
gertas

1
Tuve que cambiar la línea de análisis obj = JSON.parse(response.body.first)para que funcione.
Kimmo Lehto

55
También funciona muy bien en Rails 4 ... ¡gracias! Prefiero esto a los métodos más específicos de la biblioteca (como en la respuesta aceptada). Como de todos modos solo debes usar esto en modo de desarrollo, el rendimiento no es un gran problema.
elsurudo

3
En los carriles 5 que tenía que cambiar Rack::Utils.bytesize(pretty_str).to_sa pretty_str.bytesize.to_sy funciona muy bien!
panteo

78

La <pre>etiqueta en HTML, utilizada con JSON.pretty_generate, hará que el JSON sea bonito en su vista. Estaba tan feliz cuando mi ilustre jefe me mostró esto:

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

55
¡Tan limpio y conciso!
Sean Szurko

23

Si quieres:

  1. Prettifique todas las respuestas JSON salientes de su aplicación automáticamente.
  2. Evite contaminar el objeto # to_json / # as_json
  3. Evite analizar / volver a renderizar JSON utilizando middleware (¡YUCK!)
  4. ¡Hazlo por el CAMINO!

Entonces ... reemplace el ActionController :: Renderer para JSON! Simplemente agregue el siguiente código a su 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

Esto es increíble, pero en realidad hace que las fechas / horas se representen de manera diferente: gist.github.com/nornagon/9c24b68bd6d3e871add3
nornagon

Varios problemas con esto: (1) JSON.pretty_generate requiere json.respond_to?(:to_h)o :to_hash. (2) pretty_generate puede ahogarse en cosas que to_json no.
Christopher Oezbek

@nornagon No he aplicado este cambio y estoy obteniendo la misma diferencia que viste entre .to_json y pretty_generate. Solo lo veo en una consola de rieles, no simplemente irb. Creo que esto podría ser una cuestión general de rieles, nada que ver con este parche. Además, Time.parse devuelve el mismo resultado cuando convierte la cadena a tiempo para ambos formatos. Solo sería un inconveniente menor al buscar registros en las marcas de tiempo, pero si de todos modos estás tratando de agregar algunos \ s + no es realmente un gran problema.
con--

@nornagon parece que el problema que viste fue la redefinición de ActiveSupport de to_json, como se menciona en el comentario de Ammo Goettsch
con--

17

Echa un vistazo a Impresionante impresión . Analice la cadena JSON en un Ruby Hash, luego muéstrelo apasí:

require "awesome_print"
require "json"

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

ap(JSON.parse(json))

Con lo anterior, verá:

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

Awesome Print también agregará un color que Stack Overflow no te mostrará.


2
¡De acuerdo con usted! awesome_print es simple impresionante!
Aashish

2
Estamos usando awesome_print también para nuestros proyectos y funciona como el nombre es -> impresionante
Simon Franzen

13

Volcar un objeto ActiveRecord a JSON (en la consola de Rails):

pp User.first.as_json

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

3
para obtener una cadena en pplugar de imprimir a la salida estándar, use User.first.as_json.pretty_inspect. Funciona bien para mi
Johnny Wong

12

Usa <pre>código HTML y pretty_generatees un buen truco:

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

Si encuentra que la pretty_generateopción integrada en la biblioteca JSON de Ruby no es lo suficientemente "bonita", le recomiendo mi propia joya NeatJSON para su formateo.

Para usarlo:

gem install neatjson

y luego usar

JSON.neat_generate

en vez de

JSON.pretty_generate

Al igual que Ruby, ppmantendrá los objetos y las matrices en una línea cuando quepan, pero se ajustará a múltiples según sea necesario. Por ejemplo:

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

También es compatible con una variedad de opciones de formato para personalizar aún más su salida. Por ejemplo, ¿cuántos espacios antes y después de los dos puntos? ¿Antes / después de las comas? ¿Dentro de los corchetes de matrices y objetos? ¿Quieres ordenar las claves de tu objeto? ¿Quieres que los dos puntos estén alineados?


2
Esta gema es genial: ¡la alineación de los dos puntos es particularmente dulce!
webdevguy

8

Aquí hay una solución de middleware modificada a partir de esta excelente respuesta de @gertas . Esta solución no es específica de Rails: debería funcionar con cualquier aplicación Rack.

La técnica de middleware utilizada aquí, usando #each, se explica en ASCIIcasts 151: Rack Middleware de Eifion Bedford.

Este código va en 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

Para activarlo, agregue esto a config / entornos / test.rb y config / assets / development.rb:

config.middleware.use "PrettyJsonResponse"

Como @gertas advierte en su versión de esta solución, evite usarla en producción. Es algo lento

Probado con rieles 4.1.6.



4

Aquí está mi solución que obtuve de otras publicaciones durante mi propia búsqueda.

Esto le permite enviar la salida pp y jj a un archivo según sea necesario.

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

He usado la gema CodeRay y funciona bastante bien. El formato incluye colores y reconoce muchos formatos diferentes.

Lo he usado en una gema que se puede usar para depurar API de rieles y funciona bastante bien.

Por cierto, la gema se llama 'api_explorer' ( http://www.github.com/toptierlabs/api_explorer )


3

Si está buscando implementar esto rápidamente en una acción del controlador Rails para enviar una respuesta JSON:

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

2

Si está usando RABL , puede configurarlo como se describe aquí para usar 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

Un problema con el uso de JSON.pretty_generate es que los validadores de esquema JSON ya no estarán contentos con sus cadenas de fecha y hora. Puede arreglarlos en su config / initializers / rabl_config.rb con:

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

Utilizo lo siguiente porque encuentro que los encabezados, el estado y la salida JSON son útiles como un conjunto. La rutina de llamada se desglosa por recomendación de una presentación de railscasts en: 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

Bonita variante de impresión:

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

Resultado:

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

0

El ejemplo más simple, podría pensar en:

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

Ejemplo de consola de rieles:

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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.