Bagaimana cara menimpa to_json di Rails?

94

Memperbarui:

Masalah ini tidak dieksplorasi dengan benar. Masalah sebenarnya ada di dalam render :json.

Tempel kode pertama dalam pertanyaan asli akan menghasilkan hasil yang diharapkan. Namun, masih ada keberatan. Lihat contoh ini:

render :json => current_user

adalah TIDAK sama dengan

render :json => current_user.to_json

Artinya, render :jsontidak akan secara otomatis memanggil to_jsonmetode yang terkait dengan objek Pengguna. Faktanya , jika model to_jsondiganti User, render :json => @userakan menghasilkan seperti yang ArgumentErrordijelaskan di bawah ini.

ringkasan

# 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

Ini semua tampak konyol bagiku. Ini sepertinya memberi tahu saya bahwa rendersebenarnya tidak memanggil Model#to_jsonketika tipe :jsonditentukan. Adakah yang bisa menjelaskan apa yang sebenarnya terjadi di sini?

Setiap genii yang dapat membantu saya dengan ini kemungkinan dapat menjawab pertanyaan saya yang lain: Bagaimana membangun respons JSON dengan menggabungkan @ foo.to_json (opsi) dan @ bars.to_json (opsi) di Rails


Pertanyaan Asli:

Saya telah melihat beberapa contoh lain di SO, tetapi saya tidak melakukan apa yang saya cari.

Saya mencoba:

class User < ActiveRecord::Base

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

end

Aku mulai ArgumentError: wrong number of arguments (1 for 0)di

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

Ada ide?

maček
sumber
Contoh Anda berfungsi di salah satu model saya. Apakah salah satu metode username, fooatau barmengharapkan argumen?
Jonathan Julian
Tidak, usernameini bukan metode dan foodan bartidak memerlukan metode. Saya memperbarui pertanyaan saya untuk menunjukkan di mana kesalahan itu terjadi.
maček
Saya menjalankan 1.8.7. Anda harus membuka file itu dan melihat mengapa itu meneruskan arg ke metode yang mengharapkan nol args.
Jonathan Julian

Jawaban:

216

Anda mendapatkan ArgumentError: wrong number of arguments (1 for 0)karena to_jsonperlu diganti dengan satu parameter, optionshash.

def to_json(options)
  ...
end

Penjelasan lebih lama dari to_json, as_json, dan rendering:

Di ActiveSupport 2.3.3, as_jsonditambahkan untuk mengatasi masalah seperti yang Anda temui. The penciptaan dari json harus terpisah dari render dari json tersebut.

Sekarang, kapan saja to_jsondipanggil pada suatu objek, as_jsondipanggil untuk membuat struktur data, dan hash itu dikodekan sebagai string JSON menggunakan ActiveSupport::json.encode. Ini terjadi untuk semua jenis: objek, numerik, tanggal, string, dll (lihat kode ActiveSupport).

Objek ActiveRecord berperilaku dengan cara yang sama. Ada as_jsonimplementasi default yang membuat hash yang menyertakan semua atribut model. Anda harus mengganti as_jsonModel Anda untuk membuat struktur JSON yang Anda inginkan . as_json, seperti yang lama to_json, mengambil opsi hash di mana Anda dapat menentukan atribut dan metode untuk disertakan secara deklaratif.

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

Di pengontrol Anda, render :json => odapat menerima string atau objek. Jika itu sebuah string, itu dilewatkan sebagai tubuh respons, jika itu sebuah objek, to_jsondisebut, yang memicu as_jsonseperti dijelaskan di atas.

Jadi, selama model Anda diwakili dengan benar dengan as_jsonpenggantian (atau tidak), kode pengontrol Anda untuk menampilkan satu model akan terlihat seperti ini:

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

Moral dari cerita ini adalah: Hindari menelepon to_jsonsecara langsung, izinkan renderuntuk melakukan itu untuk Anda. Jika Anda perlu mengubah keluaran JSON, panggil as_json.

format.json { render :json => 
    @user.as_json(:only => [:username], :methods => [:avatar]) }
Jonathan Julian
sumber
@Jonathan Julian, ini adalah penjelasan yang sangat membantu as_json. Seperti yang Anda lihat di dokumen ActiveRecord :: Serialization ( api.rubyonrails.org/classes/ActiveRecord/… ), hanya ada sedikit (tidak) dokumentasi untuk ini. Saya akan mencobanya :)
maček
1
@Jonathan Julian, jika saya dapat memilih 10 kali ini, saya akan melakukannya. Dimana sih as_jsondokumennya! Terima kasih lagi :)
maček
71

Jika Anda mengalami masalah dengan ini di Rails 3, serializable_hashganti as_json. Ini akan mendapatkan format XML Anda secara gratis juga :)

Aku butuh waktu lama untuk memikirkannya. Harapan yang membantu seseorang.

Sam Soffes
sumber
1
Adakah yang tahu ada artikel bagus tentang metode ini serializable_hash? Ketika saya menggunakannya, itu mengubah keluaran xml saya berikutnya dari membungkus objek dengan namanya (misalnya "kutipan" untuk objek kutipan ") menjadi sebagai gantinya selalu membungkusnya dengan" <hash> ".
Tyler Collier
@Tylerollier itu harus opsi yang sama sepertito_xml
Sam Soffes
Terima kasih untuk solusi ini! Saya menggunakan ruby2 / rails4 dan as_json tidak bekerja dengan objek bersarang, metode yang diganti tidak dipanggil dalam 'include', dengan serializable_hash berfungsi!
santuxus
Lihat robots.thoughtbot.com/better-serialization-less-as-json untuk penjelasan tentang mengapa serializable_hash harus diganti.
Topher Hunt
36

Untuk orang yang tidak ingin mengabaikan opsi pengguna tetapi juga menambahkan opsi mereka:

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

Semoga ini bisa membantu siapa saja :)

Danpe
sumber
1
Ini adalah cara saya melakukannya, kecuali saya menetapkan optionshash secara default = {}sehingga tidak diperlukan saat menelepon
mroach
4

Timpa bukan to_json, tapi as_json. Dan dari as_json panggil apa yang Anda inginkan:

Coba ini:

def as_json 
 { :username => username, :foo => foo, :bar => bar }
end
glebm
sumber
Bukankah as_jsonhanya untuk ActiveResource?
Jonathan Julian
Rupanya ActiveRecord :: Serialization memiliki as_json api.rubyonrails.org/classes/ActiveRecord/Serialization.html
glebm
@glebm, saya mencoba ini dan saya mendapatkan hasil yang sama. Saya memperbarui pertanyaan saya untuk ditunjukkan kepada Anda.
maček
@glebm, saya masih mendapatkan kesalahan yang sama persis. Bahkan ketika saya melakukannya, render :json => current_usersaya mendapatkan hasil default yang diharapkan (semua atribut untuk Usermodel dalam format JSON). Ketika saya menambahkan as_jsonmetode ke Usermodel saya dan mencoba hal yang sama, saya mendapatkan kesalahan :(
maček
@glebm, terima kasih. Saya tahu saya melakukan sesuatu yang salah. Mungkin ada baiknya memeriksa pertanyaan yang diperbarui.
maček