Как переопределить 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