So formatieren Sie JSON-Ausgaben in Ruby on Rails „hübsch“

627

Ich möchte, dass meine JSON-Ausgabe in Ruby on Rails "hübsch" oder schön formatiert ist.

Im Moment rufe ich an to_jsonund mein JSON ist alles in einer Leitung. Manchmal kann es schwierig sein, festzustellen, ob im JSON-Ausgabestream ein Problem vorliegt.

Gibt es eine Möglichkeit, mein JSON so zu konfigurieren, dass es in Rails "hübsch" oder schön formatiert ist?

JP Richardson
quelle
2
Sie sind sich nicht sicher, wo Sie es sehen, aber in der Konsole des Webkits wird aus jedem protokollierten oder angeforderten JSON ein schöner Baum erstellt.
Ryan Florence
8
Beachten Sie dabei, dass die Größe Ihres JSON-Inhalts aufgrund des zusätzlichen Leerzeichens sprunghaft ansteigt. In einer Entwicklungsumgebung ist es oft hilfreich, dass JSON einfach zu lesen ist. In einer Produktionsumgebung möchten Sie jedoch, dass Ihre Inhalte so schlank wie möglich sind, um Geschwindigkeit und Reaktionsfähigkeit im Browser des Benutzers zu gewährleisten.
der Blechmann
2
Verwenden Sie y my_jsonwird gut formatieren Sachen, wenn Sie eine schnelle Lösung wollen.
Randomor
5
@ Randomorundefined method 'y' for main:Object
Nurettin
yist in der Schienenkonsole erhältlich.
Sophia Feng

Antworten:

1000

Verwenden Sie die pretty_generate()in späteren Versionen von JSON integrierte Funktion. Zum Beispiel:

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

Was bringt dich:

{
  "array": [
    1,
    2,
    3,
    {
      "sample": "hash"
    }
  ],
  "foo": "bar"
}
Lammhaanxy
quelle
32
Raffiniert! Ich habe dies in meine ~ / .irbrc: def json_pp (json) setzt JSON.pretty_generate (JSON.parse (json)) Ende
TheDeadSerious
10
Um dies in Rails nützlich zu machen, sollten Sie anscheinend eine Antwort geben, die Code enthält, der im selben Kontext wieformat.json { render :json => @whatever }
iconoclast
9
Sicherlich sollte Prettyprinting nur für das serverseitige Debuggen verwendet werden? Wenn Sie den obigen Code in einen Controller einfügen, haben Sie in allen Antworten eine Menge nutzloser Leerzeichen, die nicht einmal für das clientseitige Debuggen benötigt werden, da alle Tools, die ihr Geld wert sind (z. B. Firebug), bereits das Pronprinting von JSON verarbeiten.
Lambshaanxy
8
@jpatokal: Sie könnten andere bessere Optionen in Betracht ziehen, aber die Frage war, wie dies in Rails funktioniert. Zu sagen "Sie wollen das nicht in Rails tun" ist keine Antwort. Offensichtlich wollen viele Leute das in Rails machen.
Bilderstürmer
39
Das Originalposter sagte nichts darüber aus, wo er dies in einer Rails-App verwenden möchte, also antwortete ich mit einer Zeile Ruby, die überall funktionieren wird. Um damit die JSON-Antwort in einem Rails- Controller zu generieren , haben Sie bereits Ihre eigene Frage beantwortet : format.json { render :json => JSON.pretty_generate(my_json) }.
Lambshaanxy
78

Dank Rack Middleware und Rails 3 können Sie für jede Anforderung hübsches JSON ausgeben, ohne den Controller Ihrer App zu ändern. Ich habe ein solches Middleware-Snippet geschrieben und bekomme JSON in Browser und curlAusgabe schön gedruckt .

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

Der obige Code sollte in app/middleware/pretty_json_response.rbIhr Rails-Projekt eingefügt werden. Und der letzte Schritt ist die Registrierung der Middleware in config/environments/development.rb:

config.middleware.use PrettyJsonResponse

Ich empfehle nicht, es in zu verwendenproduction.rb . Das JSON-Reparsing kann die Antwortzeit und den Durchsatz Ihrer Produktions-App beeinträchtigen. Möglicherweise wird eine zusätzliche Logik wie der Header 'X-Pretty-Json: true' eingeführt, um die Formatierung für manuelle Curl-Anforderungen bei Bedarf auszulösen.

(Getestet mit Rails 3.2.8-5.0.0, Ruby 1.9.3-2.2.0, Linux)

gertas
quelle
2
Wie kommen Sie um die Neudefinition von to_json durch ActiveSupport herum? Dies hält mich davon ab, hübsch zu drucken, während ActiveSupport vorhanden ist.
Munition Göttsch
1
Ich interessiere mich nicht wirklich, to_json, as_json, jbuilder , die ich meist verwenden - was auch immer, Middleware verwandelt jedes JSON ausgegeben. Ich versuche nach Möglichkeit zu vermeiden, Klassen zu eröffnen.
Gertas
1
Ich musste die Analysezeile ändern obj = JSON.parse(response.body.first), damit es funktioniert.
Kimmo Lehto
5
Funktioniert auch in Rails 4 hervorragend ... danke! Ich ziehe dies den bibliotheksspezifischeren Methoden vor (wie in der akzeptierten Antwort). Da Sie dies sowieso nur im Dev-Modus verwenden sollten, ist der Leistungseinbruch keine große Sache.
Elsurudo
3
In Rails 5 musste ich ändern Rack::Utils.bytesize(pretty_str).to_szu pretty_str.bytesize.to_sund es funktioniert super!
Panteo
77

Das <pre>mit verwendete HTML-Tag JSON.pretty_generatemacht den JSON aus Ihrer Sicht hübsch. Ich war so glücklich, als mein berühmter Chef mir Folgendes zeigte:

<% if @data.present? %>
   <pre><%= JSON.pretty_generate(@data) %></pre>
<% end %>
Roger Garza
quelle
5
So sauber und prägnant!
Sean Szurko
23

Wenn du möchtest:

  1. Verschönern Sie alle ausgehenden JSON-Antworten von Ihrer App automatisch.
  2. Vermeiden Sie es, das Objekt # to_json / # as_json zu verschmutzen
  3. Vermeiden Sie das Parsen / erneute Rendern von JSON mithilfe von Middleware (YUCK!).
  4. Mach es auf den RAILS WAY!

Dann ... ersetzen Sie den ActionController :: Renderer für JSON! Fügen Sie Ihrem ApplicationController einfach den folgenden Code hinzu:

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
Ed Lebert
quelle
Das ist großartig, aber es führt tatsächlich dazu, dass Datum / Uhrzeit
nornagon
Einige Probleme damit: (1) JSON.pretty_generate erfordert json.respond_to?(:to_h)oder :to_hash. (2) Pretty_generate kann an Dingen ersticken, die to_json nicht tut.
Christopher Oezbek
@nornagon Ich habe diese Änderung nicht angewendet und erhalte den gleichen Unterschied, den Sie zwischen .to_json und pretty_generate gesehen haben. Ich sehe es nur in einer Rails-Konsole, nicht in einer einfachen irb. Ich denke, dies könnte eine allgemeine Rails-Sache sein, die nichts mit diesem Patch zu tun hat. Außerdem gibt Time.parse das gleiche Ergebnis zurück, wenn Sie die Zeichenfolge für beide Formate wieder in die Zeit konvertieren. Es wäre nur eine kleine Unannehmlichkeit, wenn Sie Protokolle nach Zeitstempeln durchsuchen, aber wenn Sie trotzdem ein paar \ s + hinzufügen, ist das keine große Sache.
con--
@nornagon sieht so aus, als ob das Problem, das Sie gesehen haben, die Neudefinition von to_json durch ActiveSupport war, wie in Ammo Goettschs Kommentar erwähnt
con--
17

Schauen Sie sich Awesome Print an . Analysieren Sie die JSON-Zeichenfolge in einen Ruby-Hash und zeigen Sie sie dann folgendermaßen an ap:

require "awesome_print"
require "json"

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

ap(JSON.parse(json))

Mit den oben genannten sehen Sie:

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

Mit Awesome Print wird auch eine Farbe hinzugefügt, die Stack Overflow nicht anzeigt.

Synthead
quelle
2
Ich stimme dir zu! awesome_print ist einfach genial!
Aashish
2
Wir verwenden awesome_print auch für unsere Projekte und es funktioniert so, als wäre der Name -> awesome
Simon Franzen
13

Dumping eines ActiveRecord-Objekts an JSON (in der Rails-Konsole):

pp User.first.as_json

# => {
 "id" => 1,
 "first_name" => "Polar",
 "last_name" => "Bear"
}
Thomas Klemm
quelle
3
ppVerwenden Sie , um eine Zeichenfolge zu erhalten, anstatt auf die Standardausgabe zu drucken User.first.as_json.pretty_inspect. Funktioniert gut für mich.
Johnny Wong
12

Verwenden von <pre>HTML-Code und pretty_generateist ein guter Trick:

<%
  require 'json'

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

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

Wenn Sie feststellen, dass die pretty_generatein Rubys JSON-Bibliothek integrierte Option nicht "hübsch" genug ist, empfehle ich mein eigenes NeatJSON- Juwel für Ihre Formatierung.

Um es zu benutzen:

gem install neatjson

und dann verwenden

JSON.neat_generate

anstatt

JSON.pretty_generate

Wie bei Ruby ppwerden Objekte und Arrays in einer Zeile gehalten, wenn sie passen, aber bei Bedarf in mehrere Zeilen umbrochen. Zum Beispiel:

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

Es unterstützt auch eine Vielzahl von Formatierungsoptionen , um Ihre Ausgabe weiter anzupassen. Wie viele Leerzeichen vor / nach Doppelpunkten? Vor / nach Kommas? In den Klammern von Arrays und Objekten? Möchten Sie die Schlüssel Ihres Objekts sortieren? Möchten Sie, dass alle Doppelpunkte in einer Reihe stehen?

Phrogz
quelle
2
Dieser Edelstein rockt - die Ausrichtung auf Doppelpunkten ist besonders süß!
Webdevguy
8

Hier ist eine Middleware-Lösung, die von dieser hervorragenden Antwort von @gertas modifiziert wurde . Diese Lösung ist nicht Rails-spezifisch - sie sollte mit jeder Rack-Anwendung funktionieren.

Die hier verwendete Middleware-Technik unter Verwendung von #each wird in ASCIIcasts 151: Rack Middleware von Eifion Bedford erläutert .

Dieser Code befindet sich in 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

Fügen Sie dies zum Aktivieren zu config / environment / test.rb und config / environment / development.rb hinzu:

config.middleware.use "PrettyJsonResponse"

Vermeiden Sie die Verwendung in der Produktion, da @gertas in seiner Version dieser Lösung warnt. Es ist etwas langsam.

Mit Schienen getestet 4.1.6.

Wayne Conrad
quelle
5
#At Controller
def branch
    @data = Model.all
    render json: JSON.pretty_generate(@data.as_json)
end
Буянбат Чойжилснрэн
quelle
4

Hier ist meine Lösung, die ich während meiner eigenen Suche aus anderen Posts abgeleitet habe.

Auf diese Weise können Sie die pp- und jj-Ausgabe nach Bedarf an eine Datei senden.

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
Christopher Mullins
quelle
3

Ich habe das Juwel CodeRay verwendet und es funktioniert ziemlich gut. Das Format enthält Farben und erkennt viele verschiedene Formate.

Ich habe es für ein Juwel verwendet, das zum Debuggen von Rails-APIs verwendet werden kann, und es funktioniert ziemlich gut.

Der Edelstein heißt übrigens 'api_explorer' ( http://www.github.com/toptierlabs/api_explorer )

Tony
quelle
3

Wenn Sie dies schnell in einer Rails-Controller-Aktion implementieren möchten, um eine JSON-Antwort zu senden:

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

Wenn Sie RABL verwenden , können Sie es wie hier beschrieben für die Verwendung von JSON.pretty_generate konfigurieren:

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

Ein Problem bei der Verwendung von JSON.pretty_generate besteht darin, dass JSON-Schemaüberprüfer mit Ihren datetime-Zeichenfolgen nicht mehr zufrieden sind. Sie können diese in Ihrer config / initializers / rabl_config.rb beheben mit:

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
Jim Flood
quelle
2

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

Ich verwende Folgendes, da ich die Header, den Status und die JSON-Ausgabe als Satz nützlich finde. Die Anrufroutine wird auf Empfehlung einer Railscasts-Präsentation unter http://railscasts.com/episodes/151-rack-middleware?autoplay=true aufgeschlüsselt

  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
TheDadman
quelle
1

Hübsche Druckvariante:

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

Ergebnis:

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

Das einfachste Beispiel, an das ich denken könnte:

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

Beispiel für eine Rails-Konsole:

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
Martin Carstens
quelle