Как переопределить to_json в Rails? - PullRequest
92 голосов
/ 03 апреля 2010

Обновление:

Эта проблема не была должным образом исследована. Реальная проблема заключается в 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

Есть идеи?

Ответы [ 4 ]

208 голосов
/ 04 апреля 2010

Вы получаете 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]) }
71 голосов
/ 02 сентября 2010

Если у вас есть проблемы с этим в Rails 3, переопределите serializable_hash вместо as_json. Это также даст вам бесплатное форматирование XML:)

Это заняло у меня целую вечность, чтобы понять. Надеюсь, это кому-нибудь поможет.

32 голосов
/ 04 апреля 2013

Для людей, которые не хотят игнорировать параметры пользователей, но также добавляют их:

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

Надеюсь, это кому-нибудь поможет:)

3 голосов
/ 03 апреля 2010

Переопределить не to_json, а as_json. А из as_json назови что хочешь:

Попробуйте это:

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