无法从JSON字符串反序列化对象(但仅限于Hash)?

时间:2013-12-06 15:07:17

标签: ruby json deserialization

我在Mac上编写了dictation gem,反序列化工作正常。当我在另一台Mac上安装它时,它将无法工作,因为它失败了#34;反序列化对象,因为它只能反序列化为哈希。

  • Private Mac Ruby版本:ruby-1.9.3-p0,json v1.8.0
  • 另一个Mac Ruby版本:ruby-1.9.3-p448,json v1.8.0

我也尝试过两种不同的Ruby版本和Gem版本,但它们都不起作用,只是我第一次编写它的初始版本。

当我在工作环境中尝试此代码时:

require 'json'

class Word
  attr_accessor :value, :translation

  def initialize(value, translation)
    @value = value
    @translation = translation
  end

  def to_json(*args)
    {
      'json_class' => self.class.name,
      'data'       => [ @value, @translation ]
    }.to_json(*args)
  end

  class << self
    def json_create(object)
      new(*object['data'])
    end
  end
end

str = '{"json_class":"Word","data":["Morgen","Tomorrow"]}'
p JSON.parse(str)

它打印一个Word对象,这是预期的:

#<Word:0x007fcce22c9c58 @translation="Tomorrow", @value="Morgen">

对于其他环境,它总是打印一个哈希:

{"json_class"=>"Word", "data"=>["Morgen", "Tomorrow"]}

我也尝试传递:object_class key,它引发了另一个例外:

p JSON.parse(str, :object_class => Word)
# => ArgumentError: wrong number of arguments (0 for 2)

我无法在运行时使用以下内容找出require 'json'版本:

puts Gem.loaded_specs['json'].version

因为Gem.loaded_specs.keys不包含它。

感谢任何提示。

3 个答案:

答案 0 :(得分:1)

从JSON lib的作者那里回复 - 由于安全原因,在较新版本上反序列化自定义对象,您可以:

JSON.parse(str, :create_additions => true)

或者你可以:

JSON.load(str)

所以,我忽略了ruby-doc中的JSON#load部分:

load(source, proc = nil, options = {})
  

从JSON源加载ruby数据结构并将其返回。来源   可以是类似字符串的对象,类似IO的对象,也可以是对象   响应read方法。如果给出了proc,它将被调用   首先以深度递归方式将任何嵌套的Ruby对象作为参数   订购。要修改默认选项,请在可选选项中传递   论证也是如此。

     

注意:此方法用于序列化来自可信用户的数据   输入,例如来自您自己的数据库服务器或您的客户端   控制,允许不受信任的用户传递JSON可能是危险的   源于它。可以通过更改解析器的默认选项   :: load_default_options方法。

     

此方法是加载/转储接口实现的一部分   元帅和YAML。

答案 1 :(得分:0)

直接反序列化为富对象(特别是如果您的JSON来自未知来源)可能是一个非常严重的攻击向量(最近的Rails漏洞与此相关)。

我猜这个功能在Ruby版本之间被禁用,或者至少改为基于白名单的方法。我找不到任何支持这种说法的链接,所以我可能错了。

无论如何,您可能会发现从反序列化的哈希中初始化类更简单,更兼容:

class Word
  def self.from_json(json)
    args = JSON.parse(json)["data"];
    new(*args)
  end
end

答案 2 :(得分:0)

这是另一种解决方法,因为我的代码不用于网络通信,因此漏洞不是问题。

在我做之前:

JSON.parse(str)

现在只需添加几行:

obj = JSON.parse(str)
if obj.is_a?(Hash)
  class_name = obj['json_class'].split('::').inject(Kernel) { |namespace, const_name| namespace.const_get(const_name) }
  args = obj['data']
  word = class_name.new(*args)
else
  word = obj
end