Comment puis-je "joliment" formater ma sortie JSON dans Ruby on Rails?
-
01-07-2019 - |
Question
J'aimerais que ma sortie JSON dans Ruby on Rails soit "jolie". ou bien formaté.
À l'heure actuelle, j'appelle to_json
et mon JSON est sur une seule ligne. Il peut parfois être difficile de savoir s’il existe un problème dans le flux de sortie JSON.
Existe-t-il un moyen de configurer ou une méthode pour rendre mon code JSON "jolie" ou bien formaté dans Rails?
La solution
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"
}
Autres conseils
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 bien imprimé dans le navigateur et une 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 / environment / 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. Finalement, 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)
La balise < pre >
en HTML, utilisée avec JSON.pretty_generate
, rendra le JSON joli dans votre vue. J'étais si heureux quand mon illustre patron m'a montré ceci:
<% if @data.present? %>
<pre><%= JSON.pretty_generate(@data) %></pre>
<% end %>
Si vous souhaitez:
- Confirmez automatiquement toutes les réponses JSON sortantes de votre application.
- Évitez de polluer les objets # to_json / # as_json
- Évitez d'analyser / de restituer le rendu JSON à l'aide d'un middleware (YUCK!)
- Faites-le comme il se doit!
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
Découvrez awesome_print . Analysez la chaîne JSON dans un Ruby Hash, puis affichez-la avec awesome_print comme suit:
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 une couleur que Stack Overflow ne vous montrera pas:)
Transfert d'un objet ActiveRecord vers JSON (dans la console Rails):
pp User.first.as_json
# => {
"id" => 1,
"first_name" => "Polar",
"last_name" => "Bear"
}
Si vous (comme moi), vous trouvez que l'option pretty_generate
intégrée à la bibliothèque JSON de Ruby n'est pas "jolie". assez, je recommande mon propre NeatJSON
pour votre mise en forme.
Pour l'utiliser gem installer neatjson
puis utiliser JSON.neat_generate
au lieu de JSON.pretty_generate
.
Comme pour pp
de Ruby, il gardera les objets et les tableaux sur une seule ligne quand ils conviendront, mais en superposera plusieurs si nécessaire. 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 diverses 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?
Utiliser < pre >
, le code HTML et pretty_generate
est une bonne astuce:
<%
require 'json'
hash = JSON[{hey: "test", num: [{one: 1, two: 2, threes: [{three: 3, tthree: 33}]}]}.to_json]
%>
<pre>
<%= JSON.pretty_generate(hash) %>
</pre>
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 de middleware utilisée ici, à l'aide de #each, est expliquée à l'adresse ASCIIcasts 151: Middleware Rack de Eifion Bedford.
Ce code est placé 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 / environnements / 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.
#At Controller
def branch
@data = Model.all
render json: JSON.pretty_generate(@data.as_json)
end
Voici la solution que j'ai tirée d'autres publications au cours de ma propre recherche.
Ceci 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
J'ai utilisé la gemme CodeRay et cela fonctionne plutôt bien. Le format inclut les couleurs et reconnaît de nombreux formats différents.
Je l'ai utilisé sur une gemme qui peut être utilisée pour le débogage des API de rails et cela fonctionne plutôt bien.
À propos, la gemme s'appelle 'api_explorer' ( http://www.github.com/ toptierlabs / api_explorer )
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
J'utilise ce qui suit lorsque je trouve les en-têtes, l'état et la sortie JSON utiles en tant que un ensemble. La routine d’appel est interrompue 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
Si vous utilisez RABL , vous pouvez le configurer comme décrit 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
# 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
Variante d'impression jolie:
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}}