Come sovrascrivere to_json in Rails?


94

Aggiornare:

Questo problema non è stato adeguatamente esplorato. Il vero problema sta all'interno render :json.

La prima incolla di codice nella domanda originale produrrà il risultato atteso. Tuttavia, c'è ancora un avvertimento. Guarda questo esempio:

render :json => current_user

NON è lo stesso di

render :json => current_user.to_json

Cioè, render :jsonnon chiamerà automaticamente il to_jsonmetodo associato all'oggetto Utente. Infatti , se to_jsonviene sovrascritto sul Usermodello, render :json => @userverrà generato quanto ArgumentErrordescritto di seguito.

sommario

# 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

Tutto questo mi sembra sciocco. Questo sembra dirmi che rendernon sta effettivamente chiamando Model#to_jsonquando :jsonviene specificato il tipo . Qualcuno può spiegare cosa sta realmente succedendo qui?

Qualsiasi genio che può aiutarmi in questo può probabilmente rispondere alla mia altra domanda: come costruire una risposta JSON combinando @ foo.to_json (opzioni) e @ bars.to_json (opzioni) in Rails


Domanda originale:

Ho visto altri esempi su SO, ma nessuno fa quello che cerco.

Sto provando:

class User < ActiveRecord::Base

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

end

Mi sto ArgumentError: wrong number of arguments (1 for 0)in

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

Qualche idea?


Il tuo esempio funziona in uno dei miei modelli. Eseguite una delle username, fooo barmetodi aspettano argomenti?
Jonathan Julian

No, usernamenon è un metodo e fooe barnon richiedono metodi. Ho aggiornato la mia domanda per mostrare dove si sta verificando l'errore.
maček

Sto eseguendo 1.8.7. Dovrai aprire quel file e vedere perché sta passando un arg a un metodo che prevede zero argomenti.
Jonathan Julian

Risposte:


214

Stai ottenendo ArgumentError: wrong number of arguments (1 for 0)perché to_jsondeve essere sovrascritto con un parametro, l' optionshash.

def to_json(options)
  ...
end

Più lunga spiegazione to_json, as_jsone il rendering:

In ActiveSupport 2.3.3, è as_jsonstato aggiunto per risolvere problemi come quello che hai riscontrato. La creazione del json dovrebbe essere separata dal rendering del json.

Ora, ogni volta che to_jsonviene chiamato su un oggetto, as_jsonviene richiamato per creare la struttura dei dati e quindi quell'hash viene codificato come stringa JSON utilizzando ActiveSupport::json.encode. Questo accade per tutti i tipi: oggetto, numerico, data, stringa, ecc. (Vedere il codice ActiveSupport).

Gli oggetti ActiveRecord si comportano allo stesso modo. Esiste as_jsonun'implementazione predefinita che crea un hash che include tutti gli attributi del modello. Devi eseguire l'override as_jsonnel tuo modello per creare la struttura JSON che desideri . as_json, proprio come il vecchio to_json, accetta un'opzione hash in cui è possibile specificare attributi e metodi da includere in modo dichiarativo.

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

Nel tuo controller, render :json => opuò accettare una stringa o un oggetto. Se è una stringa, viene passata come il corpo della risposta, se è un oggetto, to_jsonviene chiamato, che si attiva as_jsoncome spiegato sopra.

Quindi, fintanto che i tuoi modelli sono rappresentati correttamente con as_jsonsostituzioni (o meno), il codice del controller per visualizzare un modello dovrebbe essere simile a questo:

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

La morale della storia è: evita di chiamare to_jsondirettamente, permetti renderdi farlo per te. Se è necessario modificare l'output JSON, chiamare as_json.

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

@ Jonathan Julian, questa è una spiegazione molto utile di as_json. Come puoi vedere nei documenti di ActiveRecord :: Serialization ( api.rubyonrails.org/classes/ActiveRecord/… ), c'è pochissima (nessuna) documentazione per questo. Lo proverò :)
maček

1
@ Jonathan Julian, se potessi votare 10 volte, lo farei. Dove diavolo sono i as_jsondocumenti! Grazie ancora :)
maček

71

Se hai problemi con questo in Rails 3, sovrascrivi serializable_hashinvece di as_json. Questo otterrà anche la formattazione XML gratuitamente :)

Mi ci è voluta un'eternità per capirlo. Spero che aiuti qualcuno.


1
Qualcuno sa di commenti positivi sul metodo serializable_hash? Quando lo uso, cambia il mio successivo output xml dall'avvolgere l'oggetto con il suo nome (ad esempio "quote" per un oggetto quote ") per racchiuderlo sempre invece con" <hash> ".
Tyler Collier

@TylerCollier dovrebbero essere le stesse opzioni dito_xml
Sam Soffes

Grazie per questa soluzione! Sto usando ruby2 / rails4 e as_json non funzionava con oggetti annidati, il metodo sovrascritto non è stato chiamato in 'include', con serializable_hash funziona!
santuxus

Vedi robots. Thoughtbot.com/better-serialization-less-as-json per una spiegazione del motivo per cui serializable_hash dovrebbe essere sostituito.
Topher Hunt

36

Per le persone che non vogliono ignorare le opzioni degli utenti ma aggiungono anche le loro:

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

Spero che questo aiuti chiunque :)


1
Questo è il modo in cui lo faccio, tranne che per impostazione predefinita l' optionshash con = {}quindi non è richiesto quando si chiama
mroach

4

Sostituisci non to_json, ma as_json. E da as_json chiama quello che vuoi:

Prova questo:

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

Non è as_jsonsolo per ActiveResource?
Jonathan Julian

Apparentemente ActiveRecord :: Serialization ha as_json api.rubyonrails.org/classes/ActiveRecord/Serialization.html
glebm

@glebm, ho provato questo e sto ottenendo lo stesso risultato. Ho aggiornato la mia domanda per mostrartela.
maček

@glebm, ricevo ancora lo stesso errore. Anche quando render :json => current_userottengo il risultato predefinito previsto (tutti gli attributi per il Usermodello in formato JSON). Quando aggiungo il as_jsonmetodo al mio Usermodello e provo la stessa cosa, ottengo l'errore :(
maček

@glebm, grazie. So che stavo facendo qualcosa di sbagliato. Potrebbe valere la pena controllare la domanda aggiornata.
maček
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.