Як замінити to_json у Rails?


94

Оновлення:

Ця проблема не була належним чином вивчена. Справжня проблема криється всередині render :json.

Перша вставка коду в оригінальному питанні дасть очікуваний результат. Однак тут ще є застереження. Дивіться цей приклад:

render :json => current_user

це НЕ те саме, що

render :json => current_user.to_json

Тобто не render :jsonбуде автоматично викликати to_jsonметод, пов'язаний з об'єктом User. Насправді , якщо to_jsonце замінено на Userмоделі, render :json => @userбуде створено ArgumentErrorописане нижче.

резюме

# 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

Мені все це здається безглуздо. Це, здається, говорить мені, що renderнасправді не викликає, Model#to_jsonколи :jsonвказано тип . Хтось може пояснити, що насправді тут відбувається?

Будь-який геній, який може мені допомогти в цьому, може відповісти на інше моє запитання: Як створити відповідь JSON, комбінуючи @ foo.to_json (options) та @ bars.to_json (options) у Rails


Оригінальне запитання:

Я бачив деякі інші приклади щодо SO, але жоден не робить те, що шукав.

Я намагаюсь:

class User < ActiveRecord::Base

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

end

Я отримую ArgumentError: wrong number of arguments (1 for 0)в

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

Якісь ідеї?


Ваш приклад працює в одній з моїх моделей. Чи є якісь - або з username, fooабо barметоди очікують аргументи?
Джонатан Джуліан,

Ні, usernameце не метод , а fooй barне вимагають методів. Я оновив своє запитання, щоб показати, де відбувається помилка.
maček

Я працюю 1.8.7. Вам доведеться відкрити цей файл і подивитися, чому він передає аргумент методу, який очікує нульових аргументів.
Джонатан Джуліан

Відповіді:


214

Ви отримуєте, ArgumentError: wrong number of arguments (1 for 0)тому що to_jsonпотрібно переосмислити один параметр, optionsхеш.

def to_json(options)
  ...
end

Довші пояснення to_json, as_jsonі рендеринга:

В ActiveSupport 2.3.3 as_jsonдодано для вирішення таких проблем, як ті, з якими ви стикалися. Створення в форматі JSON має бути відділене від надання в форматі JSON.

Тепер будь-який час to_jsonвикликається об’єктом, as_jsonвикликається для створення структури даних, а потім цей хеш кодується як рядок JSON за допомогою ActiveSupport::json.encode. Це відбувається для всіх типів: об'єкт, числовий, дата, рядок тощо (див. Код ActiveSupport).

Об'єкти ActiveRecord поводяться однаково. Існує as_jsonреалізація за замовчуванням, яка створює хеш, який включає всі атрибути моделі. Вам слід перевизначити as_jsonмодель, щоб створити потрібну структуру JSON . as_jsonтак само, як і старий to_json, бере хеш опції, де ви можете вказати атрибути та методи для декларативного включення.

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

У вашому контролері render :json => oможна прийняти рядок або об'єкт. Якщо це рядок, він передається як тіло відповіді, якщо це об'єкт to_json, що викликається, що спрацьовує, as_jsonяк пояснено вище.

Отже, поки ваші моделі належним чином представлені as_jsonзамінами (чи ні), код вашого контролера для відображення однієї моделі повинен виглядати так:

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

Мораль історії така: уникайте дзвінків to_jsonбезпосередньо, дозвольте renderце зробити за вас. Якщо вам потрібно налаштувати вихід JSON, зателефонуйте as_json.

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

@ Джонатан Джуліан, це дуже корисне пояснення as_json. Як ви можете бачити в документації ActiveRecord :: Серіалізація ( api.rubyonrails.org/classes/ActiveRecord/… ), документації щодо цього дуже мало (немає). Я спробую це :)
maček

1
@ Джонатан Джуліан, якби я міг проголосувати за це 10 разів, я б. Де, чорт візьми, as_jsonдокументи! Ще раз спасибі :)
maček

71

Якщо у вас проблеми з Rails 3, замініть serializable_hashзамість as_json. Це також отримає ваше форматування XML безкоштовно :)

Це зайняло мене назавжди, щоб зрозуміти. Надія, що комусь допомагає.


1
Хтось знає про якісь хороші записи про цей метод serializable_hash? Коли я використовую його, він змінює мої подальші результати xml із обгортання об'єкта з його іменем (наприклад, "quote" для об'єкта quote "), щоб замість цього завжди обертати його" <hash> ".
Tyler Collier,

@TylerCollier це повинні бути ті самі варіанти, що іto_xml
Сем Софф

Дякую за це рішення! Я використовую ruby2 / rails4, і as_json не працював із вкладеними об'єктами, перевизначений метод не викликався в 'include', з serializable_hash це працює!
santuxus

Див. Robots.thoughtbot.com/better-serialization-less-as-json для пояснення, чому замість цього слід замінити serializable_hash.
Тофер Хант

36

Для людей, які не хочуть ігнорувати параметри користувачів, але також додають свої:

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

Сподіваюся, це комусь допомагає :)


1
Це я так роблю, за винятком того, що я за замовчуванням використовую optionsхеш, = {}тому він не потрібен при дзвінку
mroach

4

Замінити не на_json, а на as_json. А з as_json зателефонуйте, що хочете:

Спробуйте це:

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

Це не as_jsonтільки для ActiveResource?
Джонатан Джуліан,

Очевидно ActiveRecord :: Серіалізація має as_json api.rubyonrails.org/classes/ActiveRecord/Serialization.html
glebm

@glebm, я спробував це, і я отримую той самий результат. Я оновив своє запитання, щоб показати вам.
maček

@glebm, я все ще отримую ту саму помилку. Навіть коли я це render :json => current_userотримую, я отримую очікуваний результат за замовчуванням (усі атрибути для Userмоделі у форматі JSON). Коли я додаю as_jsonметод до своєї Userмоделі і пробую те саме, я отримую помилку :(
maček

@glebm, дякую. Я знаю, що робив щось не так. Можливо, варто перевірити оновлене питання.
maček
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.