Как переопределить to_json в Rails?
Update:
Эта проблема не была должным образом изучена. Реальная проблема заключается в 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 (параметры) и @bars.to_json (параметры ) в 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
Любые идеи?
Ответы
Ответ 1
Вы получаете 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
. Это происходит для всех типов: object, numeric, date, string и т.д. (См. Код ActiveSupport).
Объекты ActiveRecord ведут себя одинаково. Существует реализация по умолчанию as_json
, которая создает хэш, который включает все атрибуты модели. Вы должны переопределить as_json
в своей модели, чтобы создать структуру JSON, которую вы хотите. as_json
, как и старый to_json
, принимает хеш-вариант, где вы можете указать атрибуты и методы для декларативного объявления.
def as_json(options)
# this example ignores the user 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]) }
Ответ 2
Если у вас возникают проблемы с этим в Rails 3, переопределите serializable_hash
вместо as_json
. Кроме того, вы получите бесплатное форматирование XML:)
Мне это потребовалось навсегда, чтобы понять. Надежда, которая помогает кому-то.
Ответ 3
Для людей, которые не хотят игнорировать параметры пользователей, но также добавить их:
def as_json(options)
# this example DOES NOT ignore the user options
super({:only => [:email, :handle]}.merge(options))
end
Надеюсь, это поможет любому:)
Ответ 4
Переопределить не to_json, а as_json.
И из as_json вызывают то, что вы хотите:
Попробуйте следующее:
def as_json
{ :username => username, :foo => foo, :bar => bar }
end