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?


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, 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.


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 :)


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

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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.