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?
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"
}
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)
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 %>
Si tu veux:
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é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 :)
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>
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) 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?
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.
#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 )
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
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
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
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
# 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
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}}