Wie überschreibe ich to_json in Rails?


94

Aktualisieren:

Dieses Problem wurde nicht richtig untersucht. Das eigentliche Problem liegt in render :json.

Das erste Einfügen von Code in die ursprüngliche Frage liefert das erwartete Ergebnis. Es gibt jedoch immer noch eine Einschränkung. Siehe dieses Beispiel:

render :json => current_user

ist NICHT das gleiche wie

render :json => current_user.to_json

Das heißt, render :jsondie to_jsondem Benutzerobjekt zugeordnete Methode wird nicht automatisch aufgerufen . In der Tat , wenn to_jsonauf der überschrieben wird UserModell render :json => @usergeneriert die ArgumentErrorunten beschrieben.

Zusammenfassung

# works if User#to_json is not overridden
render :json => current_user

# If User#to_json is overridden, User requires explicit call
render :json => current_user.to_json

Das alles kommt mir albern vor. Dies scheint mir zu sagen, dass rendernicht aufgerufen wird, Model#to_jsonwenn der Typ :jsonangegeben wird. Kann jemand erklären, was hier wirklich los ist?

Alle Genien, die mir dabei helfen können, können wahrscheinlich meine andere Frage beantworten: Wie erstelle ich eine JSON-Antwort, indem ich @ foo.to_json (Optionen) und @ bars.to_json (Optionen) in Rails kombiniere?


Ursprüngliche Frage:

Ich habe einige andere Beispiele auf SO gesehen, aber ich mache nicht das, wonach ich suche.

Ich versuche:

class User < ActiveRecord::Base

  # this actually works! (see update summary above)
  def to_json
    super(:only => :username, :methods => [:foo, :bar])
  end

end

Ich steige ArgumentError: wrong number of arguments (1 for 0)ein

/usr/lib/ruby/gems/1.9.1/gems/activesupport-2.3.5/lib/active_support/json/encoders/object.rb:4:in `to_json

Irgendwelche Ideen?


Ihr Beispiel funktioniert in einem meiner Modelle. Wählen Sie eine der username, foooder barMethoden Argumente erwarten?
— Jonathan Julian

Nein, usernameist kein Verfahren und foound barkeine Methoden erfordern. Ich habe meine Frage aktualisiert, um zu zeigen, wo der Fehler auftritt.
— Maček

Ich laufe 1.8.7. Sie müssen diese Datei öffnen und sehen, warum ein Argument an eine Methode übergeben wird, die keine Argumente erwartet.
— Jonathan Julian

Antworten:


214

Sie erhalten, ArgumentError: wrong number of arguments (1 for 0)weil to_jsonmit einem Parameter, dem optionsHash , überschrieben werden muss .

def to_json(options)
  ...
end

Längere Erklärung to_json, as_jsonund Rendering:

In ActiveSupport 2.3.3 as_jsonwurde hinzugefügt, um Probleme wie das aufgetretene zu beheben . Die Erstellung des JSON sollte vom Rendering getrennt sein des JSON getrennt sein.

Jetzt wird jedes Mal, wenn to_jsonein Objekt as_jsonaufgerufen wird, aufgerufen, um die Datenstruktur zu erstellen, und dann wird dieser Hash mithilfe von als JSON-Zeichenfolge codiertActiveSupport::json.encode . Dies geschieht für alle Typen: Objekt, Zahl, Datum, Zeichenfolge usw. (siehe ActiveSupport-Code).

ActiveRecord-Objekte verhalten sich genauso. Es gibt eine Standardimplementierung as_json, die einen Hash erstellt, der alle Attribute des Modells enthält. Sie sollten as_jsonin Ihrem Modell überschreiben , um die gewünschte JSON-Struktur zu erstellen . as_jsonverwendet genau wie der alte to_jsoneinen Options-Hash, in dem Sie Attribute und Methoden angeben können, die deklarativ eingeschlossen werden sollen.

def as_json(options)
  # this example ignores the user's options
  super(:only => [:email, :handle])
end

render :json => oKann in Ihrem Controller eine Zeichenfolge oder ein Objekt akzeptieren. Wenn es sich um eine Zeichenfolge handelt, wird sie als Antworttext durchlaufen, wenn es sich um ein Objekt to_jsonhandelt, das as_jsonwie oben erläutert ausgelöst wird.

Solange Ihre Modelle ordnungsgemäß mit as_jsonÜberschreibungen dargestellt werden (oder nicht), sollte Ihr Controller-Code zur Anzeige eines Modells folgendermaßen aussehen:

format.json { render :json => @user }

Die Moral der Geschichte lautet: Vermeiden Sie es, to_jsondirekt anzurufen , lassen renderSie das für Sie tun. Wenn Sie die JSON-Ausgabe optimieren müssen, rufen Sie auf as_json.

format.json { render :json => 
    @user.as_json(:only => [:username], :methods => [:avatar]) }

@ Jonathan Julian, das ist eine sehr hilfreiche Erklärung von as_json. Wie Sie in den ActiveRecord :: Serialization-Dokumenten ( api.rubyonrails.org/classes/ActiveRecord/… ) sehen können, gibt es hierfür nur sehr wenig (keine) Dokumentation. Ich werde es versuchen :)
— maček

1
@ Jonathan Julian, wenn ich dies 10 Mal abstimmen könnte, würde ich. Wo zum Teufel sind die as_jsonDokumente!
— Nochmals vielen

71

Wenn Sie in Rails 3 Probleme damit haben, überschreiben Sie serializable_hashstatt as_json. Dadurch erhalten Sie auch Ihre XML-Formatierung kostenlos :)

Ich habe ewig gebraucht, um das herauszufinden. Hoffe das hilft jemandem.


1
Kennt jemand gute Aufzeichnungen über die Methode serializable_hash? Wenn ich es verwende, ändert es meine nachfolgende XML-Ausgabe vom Umschließen des Objekts mit seinem Namen (z. B. "Zitat" für ein Anführungszeichenobjekt "), um es stattdessen immer mit" <Hash> "zu umschließen.
— Tyler Collier

@ TylerCollier sollte es die gleichen Optionen sein wieto_xml
— Sam Soffes

Danke für diese Lösung! Ich verwende ruby2 / rails4 und as_json hat nicht mit verschachtelten Objekten gearbeitet, die überschriebene Methode wurde in 'include' nicht aufgerufen, mit serializable_hash funktioniert es!
— Santuxus

Unter robots.thoughtbot.com/better-serialization-less-as-json finden Sie eine Erklärung, warum serializable_hash stattdessen überschrieben werden sollte.
— Topher Hunt

36

Für Leute, die Benutzeroptionen nicht ignorieren, sondern auch ihre hinzufügen möchten:

def as_json(options)
  # this example DOES NOT ignore the user's options
  super({:only => [:email, :handle]}.merge(options))
end

Hoffe das hilft jedem :)


1
Dies ist die Art und Weise, wie ich es mache, außer ich verwende standardmäßig den optionsHash mit, = {}so dass es nicht erforderlich ist, wenn
— ich anrufe

4

Überschreiben Sie nicht to_json, sondern as_json. Und von as_json rufen Sie an, was Sie wollen:

Versuche dies:

def as_json 
 { :username => username, :foo => foo, :bar => bar }
end

Ist nicht as_jsonnur für ActiveResource?
— Jonathan Julian

Anscheinend hat ActiveRecord :: Serialization as_json api.rubyonrails.org/classes/ActiveRecord/Serialization.html
— glebm

@glebm, ich habe es versucht und ich bekomme das gleiche Ergebnis. Ich habe meine Frage aktualisiert, um sie Ihnen zu zeigen.
— Maček

@glebm, ich bekomme immer noch genau den gleichen Fehler. Selbst wenn ich dies tue, render :json => current_usererhalte ich das erwartete Standardergebnis (alle Attribute für das UserModell im JSON-Format). Wenn ich die as_jsonMethode zu meinem UserModell hinzufüge und dasselbe versuche, erhalte ich den Fehler :(
— maček

@glebm, danke. Ich weiß, dass ich etwas falsch gemacht habe. Es könnte sich lohnen, die aktualisierte Frage zu lesen.
— Maček
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.