[Ruby-On-Rails] Wie kann ich meine JSON-Ausgabe in Ruby on Rails "hübsch" formatieren?



Answers

Dank Rack Middleware und Rails 3 können Sie für jede Anfrage hübschen JSON ausgeben, ohne einen Controller Ihrer App zu ändern. Ich habe solch ein Middleware-Snippet geschrieben und ich bekomme schön gedruckten JSON in Browser und curl Ausgabe.

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.rb Ihres Rails-Projekts app/middleware/pretty_json_response.rb . Und der letzte Schritt ist, die Middleware in config/environments/development.rb zu registrieren:

config.middleware.use PrettyJsonResponse

Ich empfehle nicht, es in production.rb . Die JSON-Reparatur kann die Reaktionszeit und den Durchsatz Ihrer Produktions-App beeinträchtigen. Schließlich kann eine zusätzliche Logik, wie der "X-Pretty-Json: true" -Kopf, eingeführt werden, um die Formatierung für manuelle Aufrollungsanforderungen auf Anforderung auszulösen.

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

Question

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

Jetzt rufe ich to_json und mein JSON ist alles in einer Zeile. Manchmal kann dies schwierig sein, wenn im JSON-Ausgabestream ein Problem auftritt.

Gibt es eine Möglichkeit zu konfigurieren oder eine Methode, um meinen JSON in Rails "hübsch" oder schön formatiert zu machen?




Wenn Sie dies schnell in einer Rails-Controller-Aktion zum Senden einer JSON-Antwort implementieren möchten:

def index
  my_json = '{ "key": "value" }'
  render json: JSON.pretty_generate( JSON.parse my_json )
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



Wenn du möchtest:

  1. Alle ausgehenden JSON-Antworten von Ihrer App automatisch prettieren
  2. Vermeiden Sie die Verschmutzung des Objekts # to_json / # as_json
  3. Vermeiden Sie es, JSON mit Middleware zu analysieren / neu zu rendern (YUCK!)
  4. Tun Sie es die Schienen Wege!

Dann ... ersetze den ActionController :: Renderer für JSON! Fügen Sie Ihrem ApplicationController 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



Ich benutze folgendes, da ich die Header, den Status und die JSON-Ausgabe als Set nützlich finde. Die Rufroutine ist auf Empfehlung einer Railscast-Präsentation unter http://railscasts.com/episodes/151-rack-middleware?autoplay=true ausgebrochen

  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



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

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

Dieser Code wird in app / middleware / pretty_json_response.rb angezeigt :

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

Um es einzuschalten, fügen Sie dies zu config / environments / test.rb und config / environments / development.rb hinzu:

config.middleware.use "PrettyJsonResponse"

Wie @gertas in seiner Version dieser Lösung warnt, vermeide es, es in der Produktion zu verwenden. Es ist etwas langsam.

Getestet mit Rails 4.1.6.




Wenn Sie (wie ich) finden, dass die in der Ruby-JSON-Bibliothek pretty_generate Option pretty_generate nicht "hübsch" genug ist, empfehle ich NeatJSON mein eigenes NeatJSON Juwel für Ihre Formatierung.

Um es zu benutzen, gem install neatjson und verwende dann JSON.neat_generate statt JSON.pretty_generate .

Wie bei Rubys pp werden Objekte und Arrays auf einer Zeile gehalten, wenn sie passen, aber bei Bedarf zu mehreren Zeilen umbrechen. Beispielsweise:

{
  "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. Zum Beispiel, wie viele Leerzeichen vor / nach Doppelpunkten? Vorher / Nachher Kommas? In Klammern von Arrays und Objekten? Möchten Sie die Schlüssel Ihres Objekts sortieren? Wollen Sie, dass die Doppelpunkte alle aneinandergereiht sind?




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

Dadurch können Sie die pp- und jj-Ausgabe bei 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



Links