Как вы справляетесь с конфликтом между ActiveSupport::JSON и гемом JSON?

Я в тупике с этой проблемой.

ActiveSupport::JSON определяет to_json для различных основных объектов, как и гем JSON. Однако реализация отличается — версия ActiveSupport принимает аргументы, а версия JSON gem — нет.

Я установил гем, для которого требовался гем JSON, и мое приложение сломалось. Проблема в том, что я использую to_json в контроллере, который возвращает список объектов, но я хочу контролировать, какие атрибуты возвращаются.

Когда код где-либо в моей системе делает require 'json', я получаю это сообщение об ошибке:

TypeError: wrong argument type Hash (expected Data)

Я пробовал пару вещей, которые я читал в Интернете, чтобы исправить это, но ничего не получалось. В итоге я переписал гем, чтобы использовать ActiveSupport::JSON.decode вместо JSON.parse.

Это работает, но неустойчиво... Я не могу разветвлять драгоценные камни каждый раз, когда хочу использовать драгоценный камень, для которого требуется драгоценный камень JSON.

Обновление: лучшим решением этой проблемы является обновление до Rails 2.3 или более поздней версии, которое исправило ее.


person Community    schedule 25.03.2009    source источник
comment
Почему этот вопрос помечен как вики сообщества?   -  person Aaron Hinni    schedule 26.03.2009
comment
Я не знаю, я просто решил попробовать это и посмотреть, что он делает.   -  person Luke Francl    schedule 26.03.2009
comment
Я почувствовал твою боль, надеюсь, когда-нибудь этот беспорядок уладится.   -  person MatthewFord    schedule 03.08.2009


Ответы (6)


Обновление Это исправление применимо только к Rails ‹ 2.3. Как упоминает Джайлз ниже, они исправили это внутри 2.3, используя почти ту же технику. Но остерегайтесь более ранней попытки json gem обеспечить совместимость с Rails (json/add/rails), которая, если потребуется явным образом, снова все сломает.

Вы имеете в виду, что сам оператор require 'json' вызывает это исключение? Или вы имеете в виду, что при вызове @something.to_json(:something => value) вы получаете сообщение об ошибке? Последнее - это то, что я ожидал, если у вас есть проблема, требующая драгоценного камня JSON, тогда я не уверен, что происходит.

Я только что столкнулся с этой проблемой с драгоценным камнем oauth. В моем случае настоящего конфликта нет, потому что гем oauth не зависит от реализации to_json. Поэтому проблема в том, что JSON затирает объявления ActiveSupport. Я решил это, просто потребовав json перед загрузкой ActiveSupport. положить

require 'json'

внутри Rails::Initializer сделал свое дело (хотя поместить его после блока НЕ ​​удалось).

Это позволяет ActiveSupport заменить реализацию JSON по умолчанию.

Теперь, если вы используете гем, который на самом деле зависит от реализации JSON to_json, тогда вы в ручье. Это, безусловно, худшее из метапрограммирования, и я бы посоветовал разработчикам гема Rails и JSON разрешить конфликт, хотя это будет болезненно, потому что одному или другому придется нарушать обратную совместимость.

В краткосрочной перспективе авторы гемов могут преодолеть разрыв, поддерживая обе реализации. Это более или менее осуществимо в зависимости от того, как драгоценный камень использует этот метод. Наихудшим сценарием является официальный форк (т.е. gem и gem-rails).

person Community    schedule 15.04.2009
comment
Да, я имею в виду, что когда я использую что-то, что требует вызова 'json', это убивает версию to_json для Rails, причиняя мне много боли. Спасибо за ваши предложения. Вам повезло с предложенным вариантом использования require 'json/add/rails'? Я не могу заставить это работать. - person Luke Francl; 17.04.2009
comment
require работает только при первом вызове, поэтому, если вы вызовете его до загрузки версии Rails, он ничего не сделает, когда плагин потребует этого. Я проверил это на практике, поместил в блок Rails::Initializer. Не знаю, что такое json/add/rails, но я не думаю, что это необходимо. - person gtd; 17.04.2009

ОБНОВЛЕНИЕ: даже в Rails 3.2 та же проблема остается нерешенной. Отвратительный хак, чтобы принудительно загрузить гем json и перезаписать его, то есть.

В конце концов я получил следующий код, чтобы полностью обойти to_json ActiveSupport. Поместите его в config/initializers/patches.rb, и вы можете сделать {}.jsonize или [].jsonize для создания строки JSON. Ни с чем не конфликтует, гарантируем.

# Undo the effect of 'active_support/core_ext/object/to_json'
require 'json'
[Object, Array, Hash].each do |klass|
  klass.class_eval <<-RUBY, __FILE__, __LINE__
    def jsonize(options = nil)
      ::JSON.generate self, :quirks_mode => true
    end
  RUBY
end

8 строк кода делают ваше приложение в 50 раз быстрее при кодировании JSON. Вероятно, вы хотите сделать то же самое. :)


У меня была похожая проблема до Rails 2.3.8.

Проблема в том, что ActiveSupport::JSON.backend = 'JSONGem' это половинчатое решение и вам все равно придется перезаписывать некоторые энкодеры самостоятельно. (ВНИМАНИЕ: для Rails 3.x, в котором используется MultiJson, он должен быть не ниже ActiveSupport::JSON.backend = :json_gem, иначе он будет просто неактивным.)

В моем случае мне нужно было перезаписать String#to_json, потому что JSON gem 1.4.3 лучше тем, что он не слепо кодирует не-ascii-но-валидные-UTF8 символы в форме "\uXXXX", где это не нужно, поэтому вы получаете более короткие байты (хорошо для сериализации) и легко читаемые результаты (на мой взгляд, "日本語" выглядит намного сексуальнее, чем "\u65e5\u672c\u8a9e").

Вот патч обезьяны, который я использовал — поместите следующий код в config/initializers/patches.rb

module ActiveSupport
  module JSON
    module Encoding
      class << self
        def escape(string)
          ::JSON.generate([string])[1..-2]
        end
      end
    end
  end
end

и вы можете использовать to_json для чего угодно — String, Array и Hash.

person Community    schedule 19.07.2010

После борьбы с этим некоторое время. Я нашел самое простое решение:

if defined?(ActiveSupport::JSON)
  [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
   klass.class_eval do
    def to_json(*args)
      super(args)
    end
    def as_json(*args)
      super(args)
    end
   end
  end
end

поместите это в любом месте после загрузки activesupport ..

person Community    schedule 07.09.2012

Я почти уверен, что они исправили это в 2.3, но я не могу вспомнить, как.

person Community    schedule 04.08.2009
comment
Да, я думаю, они избавились от прямого вызова to_json. Вместо этого вы определяете as_json или напрямую вызываете JSON.generate или ActiveSupport::JSON.encode. И с новым бэкендом JSON, я думаю, что ActiveSupport::JSON.encode будет использовать вашу предпочтительную библиотеку. - person Luke Francl; 05.08.2009

В моем, хотя и уникальном случае, у меня было приложение Ruby (не-Rails), которое фактически загружало приложение Rails (из загрузки config/environment.rb), а также некоторые драгоценные камни, которые ссылались на json. Это вызвало у меня огромные головные боли из-за того, что я не мог просто изменить файл environment.rb приложения Rails. В итоге я разветвил несколько драгоценных камней, чтобы заставить json работать, не вызывая ужасного сообщения TypeError: неправильный тип аргумента Hash (ожидаемые данные).

Мне повезло с этим решением, которое прямо противоположно ответу вики сообщества выше... http://blog.swivel.com/code/2009/03/active-support-and-json-gems-dont-play-nice.html, который в основном поддерживает вызов require 'active_support' BEFORE require 'json'

Это был единственный способ заставить его работать, и поверьте мне, я пробовал все в течение многих месяцев.

person Community    schedule 03.04.2010

Я еще не пробовал, но похоже, что Rails 2.3.3 дает вам некоторый контроль:

ActiveSupport::JSON.backend = 'JSONGem'

Найти здесь

person Community    schedule 20.01.2010
comment
Да, Rails 2.3+ решает эту проблему. - person Luke Francl; 20.01.2010