web-dev-qa-db-fra.com

Comment puis-je "joliment" formater ma sortie JSON dans Ruby sur Rails?

Je voudrais que ma sortie JSON dans Ruby sur Rails soit "jolie" ou bien formatée.

En ce moment, j'appelle to_json et mon JSON est sur une seule ligne. Il peut parfois être difficile de voir s’il ya un problème dans le flux de sortie JSON.

Existe-t-il un moyen de configurer ou une méthode pour rendre mon JSON "joli" ou bien formaté dans Rails?

583
JP Richardson

Utilisez la fonction pretty_generate(), intégrée aux versions ultérieures de JSON. Par exemple:

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

Ce qui vous amène:

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

Grâce à Rack Middleware et à Rails 3, vous pouvez générer de jolis JSON pour chaque requête sans changer aucun contrôleur de votre application. J'ai écrit un tel extrait de middleware et j’obtiens du JSON joliment imprimé dans le navigateur et la sortie 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

Le code ci-dessus doit être placé dans app/middleware/pretty_json_response.rb de votre projet Rails. Et la dernière étape consiste à enregistrer le middleware dans config/environments/development.rb:

config.middleware.use PrettyJsonResponse

Je ne recommande pas de l'utiliser dans production.rb. Le reparsing JSON peut dégrader le temps de réponse et le débit de votre application de production. Éventuellement, une logique supplémentaire telle que l'en-tête 'X-Pretty-Json: true' peut être introduite pour déclencher le formatage des demandes de curl manuelles à la demande.

(Testé avec Rails 3.2.8-5.0.0, Ruby 1.9.3-2.2.0, Linux)

71
gertas

La balise <pre> en HTML, utilisée avec JSON.pretty_generate, rendra le JSON joli dans votre vue. J'étais tellement heureuse quand mon illustre patron m'a montré ceci:

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

Si tu veux:

  1. Confirmez automatiquement toutes les réponses JSON sortantes à partir de votre application.
  2. Évitez de polluer Object # to_json/# as_json
  3. Évitez d'analyser/de restituer le rendu JSON à l'aide d'un middleware (YUCK!)
  4. Faites le Rails WAY!

Alors ... remplacez ActionController :: Renderer for JSON! Ajoutez simplement le code suivant à votre 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
20
Ed Lebert

Départ awesome_print . Analysez la chaîne JSON dans un hachage Ruby, puis affichez-la avec awesome_print comme ceci:

require "awesome_print"
require "json"

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

ap(JSON.parse(json))

Avec ce qui précède, vous verrez:

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

awesome_print ajoutera également de la couleur que Stack Overflow ne vous montrera pas :)

12
Synthead

Utiliser <pre> code HTML et pretty_generate est un bon truc:

<%
  require 'json'

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

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

Transfert d’un objet ActiveRecord vers JSON (dans la console Rails):

pp User.first.as_json

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

Si vous (comme moi) trouvez que l'option pretty_generate intégrée à la bibliothèque JSON de Ruby n'est pas "jolie", je vous recommande ma propre NeatJSON gem pour votre formatage.

Pour l'utiliser gem install neatjson puis utiliser JSON.neat_generate au lieu de JSON.pretty_generate.

Comme Ruby, pp, il gardera les objets et les tableaux sur une seule ligne quand ils conviendront, mais en placera plusieurs au besoin. Par exemple:

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

Il prend également en charge une variété de options de formatage pour personnaliser davantage votre sortie. Par exemple, combien d'espaces avant/après les deux-points? Avant/après les virgules? Dans les crochets des tableaux et des objets? Voulez-vous trier les clés de votre objet? Voulez-vous que les deux points soient alignés?

10
Phrogz

Voici une solution middleware modifiée à partir de cette excellente réponse de @gertas . Cette solution n'est pas spécifique à Rails - elle devrait fonctionner avec n'importe quelle application Rack.

La technique middleware utilisée ici, en utilisant #each, est expliquée à ASCIIcasts 151: Rack Middleware par Eifion Bedford.

Ce code va dans 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

Pour l'activer, ajoutez ceci à config/environment/test.rb et à config/environment/development.rb:

config.middleware.use "PrettyJsonResponse"

Comme @gertas le met en garde dans sa version de cette solution, évitez de l'utiliser en production. C'est un peu lent.

Testé avec Rails 4.1.6.

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

J'ai utilisé la gemme CodeRay et cela fonctionne plutôt bien. Le format inclut les couleurs et reconnaît beaucoup de formats différents.

Je l'ai utilisé sur une gemme qui peut être utilisée pour le débogage des API Rails et cela fonctionne plutôt bien.

À propos, la gemme s'appelle 'api_Explorer' ( http://www.github.com/toptierlabs/api_Explorer )

2
Tony

Si vous souhaitez implémenter rapidement cela dans une action du contrôleur Rails pour envoyer une réponse JSON:

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

Voici la solution que j'ai tirée d'autres publications au cours de ma propre recherche.

Cela vous permet d’envoyer les sorties pp et jj dans un fichier si nécessaire.

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
2
Christopher Mullins

J'utilise ce qui suit lorsque je trouve les en-têtes, l'état et la sortie JSON utiles en tant qu'ensemble. La routine d’appel est divisée sur recommandation d’une présentation Railcast à l’adresse suivante: 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
TheDadman

Si vous utilisez RABL , vous pouvez le configurer comme indiqué ici pour utiliser 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 problème lié à l'utilisation de JSON.pretty_generate est que les validateurs de schéma JSON ne seront plus satisfaits de vos chaînes datetime. Vous pouvez corriger ceux de votre config/initializers/rabl_config.rb avec:

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
1
Jim Flood

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

Jolie variante d'impression:

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

Résultat:

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