How do you deal with the conflict between ActiveSupport::JSON and the JSON gem?

荒凉一梦 提交于 2019-11-26 22:42:19

问题


I am stumped with this problem.

ActiveSupport::JSON defines to_json on various core objects and so does the JSON gem. However, the implementation is not the same -- the ActiveSupport version takes arguments and the JSON gem version doesn't.

I installed a gem that required the JSON gem and my app broke. The issue is that I'm using to_json in a controller that returns a list of objects, but I want to control which attributes are returned.

When code anywhere in my system does require 'json' I get this error message:

TypeError: wrong argument type Hash (expected Data)

I tried a couple of things that I read online to fix it, but nothing worked. I ended up re-writing the gem to use ActiveSupport::JSON.decode instead of JSON.parse.

This works but it's not sustainable...I can't be forking gems every time I want to use a gem that requires the JSON gem.

Update: The best solution of this problem is to upgrade to Rails 2.3 or higher, which fixed it.


回答1:


Update This fix is only applicable to Rails < 2.3. As Giles mentions below, they fixed this in 2.3 internally using much the same technique. But beware the json gem's earlier attempt at Rails compatibility (json/add/rails), which, if required explicitly will break everything all over again.

Do you mean the require 'json' statement itself raises that Exception? Or do you mean when you call @something.to_json(:something => value) you get the error? The latter is what I would expect, if you have a problem requiring the JSON gem then I'm not sure what's going on.

I just ran into this problem with the oauth gem. In my case, there is not a true conflict, because the oauth gem doesn't depend on to_json implementation. Therefore the problem is that JSON is clobbering the ActiveSupport declarations. I solved this by simply requiring json before ActiveSupport is loaded. Putting

require 'json'

inside the Rails::Initializer did the trick (though putting it after the block did NOT).

That allows ActiveSupport to clobber the default JSON implementation instead.

Now if you are using a gem that actually depends on the JSON implementation of to_json then you are up a creek. This is definitely the worst of meta-programming, and I would advocate for the Rails and JSON gem developers to resolve the conflict, though it will be painful because one or the other will have to break backwards compatibility.

In the short term, gem authors may be able to bridge the gap by supporting both implementations. This is more or less feasible depending on how the gem uses the method. A worst case scenario is an official fork (ie. gem and gem-rails).




回答2:


UPDATE: Even with Rails 3.2, the same problem remains unfixed. The nasty hack to forcibly load the json gem and overwrite it, that is.

Eventually I ended up with the following code, to entirely bypass ActiveSupport's to_json completely. Put it in config/initializers/patches.rb, and you can do {}.jsonize or [].jsonize to generate JSON string. No conflicts with anything, guaranteed.

# 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

The 8 lines of code make your app 50 times faster for JSON encoding. Probably you want to do the same. :)


I've been having a similar problem up until Rails 2.3.8.

The problem is that ActiveSupport::JSON.backend = 'JSONGem' is a half-assed solution and you still need to overwrite some encoders yourself. (WARNING: for Rails 3.x, which uses MultiJson, it must be ActiveSupport::JSON.backend = :json_gem at least, or it will be silently no-op.)

In my case, I needed to overwrite String#to_json because JSON gem 1.4.3 is better in that it doesn't blindly encode non-ascii-but-valid-UTF8 characters in the form of "\uXXXX" where it's not necessary, so you get shorter bytes (good for serialization) and easy-to-read results ("日本語" looks much sexier to my eyes than "\u65e5\u672c\u8a9e").

Here's the monkey patch that I've been using - put the following code in 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

and you're free to use to_json on anything - String, Array and Hash.




回答3:


After battling this for a while.. I found the simplest solution to be:

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

put that anywhere after activesupport is loaded..




回答4:


I'm pretty sure they fixed this in 2.3 but I can't remember how.




回答5:


In my albeit unique case, I had a Ruby (non-rails) app that actually loaded a Rails app (from a config/environment.rb load) as well as some gems that referenced json. This caused me huge headaches due to the fact that I could not simply alter the Rails app's environment.rb file. I ended up forking a number of gems in order to get json to work without raising the dreaded TypeError: wrong argument type Hash (expected Data) message.

I had some luck with this solution, which is exactly the opposite as the community wiki's answer above... http://blog.swivel.com/code/2009/03/active-support-and-json-gems-dont-play-nice.html which basically advocates calling require 'active_support' BEFORE require 'json'

This was the only way I could make it work, and believe me I tried everything over many months.




回答6:


I've yet to try it, but it looks like Rails 2.3.3 gives you some control:

ActiveSupport::JSON.backend = 'JSONGem'

Found here



来源:https://stackoverflow.com/questions/683989/how-do-you-deal-with-the-conflict-between-activesupportjson-and-the-json-gem

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!