Ce problème n'a pas été correctement étudié. Le vrai problème réside dans render :json
.
La première pâte de code dans la question d'origine donnera le résultat attendu. Cependant, il y a encore une mise en garde. Voir cet exemple:
render :json => current_user
[~ # ~] n'est pas [~ # ~] identique à
render :json => current_user.to_json
Autrement dit, render :json
N'appellera pas automatiquement la méthode to_json
Associée à l'objet Utilisateur. En fait, si to_json
Est surchargé sur le modèle User
, render :json => @user
Générera le ArgumentError
décrit ci-dessous.
# 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
Tout cela me semble idiot. Cela semble me dire que render
n'appelle pas réellement Model#to_json
Lorsque le type :json
Est spécifié. Quelqu'un peut-il expliquer ce qui se passe vraiment ici?
Tous les génies qui peuvent m'aider avec cela peuvent probablement répondre à mon autre question: Comment construire une réponse JSON en combinant @ foo.to_json (options) et @ bars.to_json (options) dans Rails
J'ai vu d'autres exemples sur SO, mais je ne fais pas ce que je cherche.
J'essaie:
class User < ActiveRecord::Base
# this actually works! (see update summary above)
def to_json
super(:only => :username, :methods => [:foo, :bar])
end
end
Je reçois ArgumentError: wrong number of arguments (1 for 0)
dans
/usr/lib/Ruby/gems/1.9.1/gems/activesupport-2.3.5/lib/active_support/json/encoders/object.rb:4:in `to_json
Des idées?
Vous obtenez ArgumentError: wrong number of arguments (1 for 0)
parce que to_json
doit être remplacé par un paramètre, le hachage options
.
def to_json(options)
...
end
Explication plus longue de to_json
, as_json
, et rendu:
Dans ActiveSupport 2.3.3, as_json
a été ajouté pour résoudre des problèmes comme celui que vous avez rencontré. La création du json doit être distincte du rendu du json .
Maintenant, à tout moment to_json
est appelé sur un objet, as_json
est invoqué pour créer la structure de données, puis ce hachage est codé en tant que chaîne JSON à l'aide de ActiveSupport::json.encode
. Cela se produit pour tous les types: objet, numérique, date, chaîne, etc. (voir le code ActiveSupport).
Les objets ActiveRecord se comportent de la même manière. Il y a une valeur par défaut as_json
implémentation qui crée un hachage qui inclut tous les attributs du modèle. Vous devez remplacer as_json
dans votre modèle pour créer la structure JSON souhaitée. as_json
, tout comme l'ancien to_json
, prend une option de hachage où vous pouvez spécifier des attributs et des méthodes à inclure de manière déclarative.
def as_json(options)
# this example ignores the user's options
super(:only => [:email, :handle])
end
Dans votre contrôleur, render :json => o
peut accepter une chaîne ou un objet. S'il s'agit d'une chaîne, elle est transmise en tant que corps de réponse, s'il s'agit d'un objet, to_json
est appelé, ce qui déclenche as_json
comme expliqué ci-dessus.
Donc, tant que vos modèles sont correctement représentés avec as_json
remplace (ou non), le code de votre contrôleur pour afficher un modèle devrait ressembler à ceci:
format.json { render :json => @user }
La morale de l'histoire est: Évitez d'appeler to_json
directement, autorisez render
à le faire pour vous. Si vous devez modifier la sortie JSON, appelez as_json
.
format.json { render :json =>
@user.as_json(:only => [:username], :methods => [:avatar]) }
Si vous rencontrez des problèmes avec cela dans Rails 3, remplacez serializable_hash
au lieu de as_json
. Vous obtiendrez également votre mise en forme XML gratuitement :)
Cela m'a pris une éternité pour comprendre. J'espère que cela aide quelqu'un.
Pour les personnes qui ne veulent pas ignorer les options des utilisateurs mais également ajouter les leurs:
def as_json(options)
# this example DOES NOT ignore the user's options
super({:only => [:email, :handle]}.merge(options))
end
J'espère que cela aide n'importe qui :)
Ne remplacez pas to_json, mais as_json. Et depuis as_json, appelez ce que vous voulez:
Essaye ça:
def as_json
{ :username => username, :foo => foo, :bar => bar }
end