你如何处理ActiveSupport :: JSON和JSON gem之间的冲突?

时间:2009-03-25 23:51:45

标签: ruby-on-rails ruby json activesupport

我很难解决这个问题。

ActiveSupport::JSON在各种核心对象上定义to_json,JSON gem也是如此。但是,实现方式并不相同 - ActiveSupport版本接受参数,而JSON gem版本则不接受。

我安装了一个需要JSON gem的gem,我的应用程序坏了。问题是我在一个返回对象列表的控制器中使用to_json,但我想控制返回哪些属性。

当我的系统中的任何地方的代码require 'json'时,我收到以下错误消息:

TypeError: wrong argument type Hash (expected Data)

我尝试了一些我在网上阅读的东西来修复它,但没有任何效果。我最终重新编写了宝石以使用ActiveSupport::JSON.decode代替JSON.parse

这样可行,但它不可持续......每次我想使用需要JSON宝石的宝石时,我都无法分配宝石。

更新:此问题的最佳解决方案是升级到Rails 2.3或更高版本,修复它。

6 个答案:

答案 0 :(得分:19)

更新:即使使用Rails 3.2,同样的问题仍未解决。令人讨厌的黑客强行加载json gem并覆盖它,就是这样。

最终,我最终得到了以下代码,完全绕过了ActiveSupport的to_json。将其放在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'是一个半解决方案,你仍然需要自己覆盖一些编码器。 (警告:对于使用MultiJson的Rails 3.x,它至少必须为ActiveSupport::JSON.backend = :json_gem,否则它将默认为no-op。)

在我的情况下,我需要覆盖String#to_json,因为JSON gem 1.4.3更好,因为它不会以{{1}的形式盲目编码非ascii-but-valid-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 - String,Array和Hash。

答案 1 :(得分:4)

更新 此修复程序仅适用于Rails&lt; 2.3。正如Giles在下面提到的,他们使用大致相同的技术在内部修正了这个问题。但要注意json gem早期尝试Rails兼容性json/add/rails),,如果明确要求,将再次破坏所有内容。

你的意思是require 'json'语句本身会引发异常吗?或者您的意思是当您致电@something.to_json(:something => value)时出现错误?后者是我所期望的,如果你有一个需要JSON gem的问题,那么我不确定发生了什么。

我刚用oauth gem遇到了这个问题。就我而言,没有真正的冲突,因为oauth gem不依赖于to_json实现。因此问题是JSON破坏了ActiveSupport声明。我通过在加载ActiveSupport之前简单地要求json来解决这个问题。把

require 'json'
Rails::Initializer内的

做了伎俩(虽然在块之后没有把它放进去)。

这允许ActiveSupport改为破坏默认的JSON实现。

现在,如果您正在使用实际依赖于to_json的JSON实现的gem,那么您就是一条小溪。这绝对是最糟糕的元编程,我会主张Rails和JSON gem开发人员来解决这个冲突,尽管这会很痛苦,因为其中一个必须打破向后兼容性。

在短期内,宝石作者可以通过支持两种实现来弥补差距。这或多或少可行,具体取决于gem如何使用该方法。最糟糕的情况是官方分叉(即。gemgem-rails)。

答案 2 :(得分:3)

经过一段时间的斗争......我找到了最简单的解决方案:

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后把它放在任何地方..

答案 3 :(得分:0)

我很确定他们在2.3中解决了这个问题,但我不记得是怎么回事。

答案 4 :(得分:0)

我还没试过,但看起来Rails 2.3.3给你一些控制权:

ActiveSupport::JSON.backend = 'JSONGem'

Found here

答案 5 :(得分:0)

在我虽然独一无二的情况下,我有一个Ruby(非rails)应用程序实际上加载了一个Rails应用程序(来自config / environment.rb加载)以及一些引用json的gem。由于我不能简单地改变Rails应用程序的environment.rb文件,这让我感到非常头疼。 我最终分配了许多宝石,以便让json工作而不会引发可怕的TypeError:错误的参数类型Hash(预期数据)消息。

我对这个解决方案感到非常幸运,这与上面社区维基的答案正好相反...... http://blog.swivel.com/code/2009/03/active-support-and-json-gems-dont-play-nice.html 基本上主张打电话 要求'active_support' BEFORE 要求'json'

这是我能让它发挥作用的唯一方式,相信我,我已经尝试了好几个月。