57

我有一个使用序列化列的模型:

class Form < ActiveRecord::Base
  serialize :options, Hash
end

有没有办法让这个序列化使用 JSON 而不是 YAML?

4

8 回答 8

161

在 Rails 3.1 中,您可以

class Form < ActiveRecord::Base
  serialize :column, JSON
end

希望有帮助

于 2011-08-07T08:44:55.023 回答
58

在 Rails 3.1 中,您可以将自定义编码器与serialize.

class ColorCoder
  # Called to deserialize data to ruby object.
  def load(data)
  end

  # Called to convert from ruby object to serialized data.
  def dump(obj)
  end
end

class Fruits < ActiveRecord::Base
  serialize :color, ColorCoder.new
end

希望这可以帮助。

参考:

定义serializehttps ://github.com/rails/rails/blob/master/activerecord/lib/active_record/base.rb#L556

Rails 附带的默认 YAML 编码器: https ://github.com/rails/rails/blob/master/activerecord/lib/active_record/coders/yaml_column.rb

这就是调用load发生的地方: https ://github.com/rails/rails/blob/master/activerecord/lib/active_record/attribute_methods/read.rb#L132

于 2011-05-12T14:55:16.537 回答
11

更新

请参阅下面 mid 的高评价答案以获得更合适的 Rails >= 3.1 答案。对于 Rails < 3.1,这是一个很好的答案。

可能这就是你要找的。

Form.find(:first).to_json

更新

1)安装' json'gem

gem install json

2) 创建 JsonWrapper 类

# lib/json_wrapper.rb

require 'json'
class JsonWrapper
  def initialize(attribute)
    @attribute = attribute.to_s
  end

  def before_save(record)
    record.send("#{@attribute}=", JsonWrapper.encrypt(record.send("#{@attribute}")))
  end

  def after_save(record)
    record.send("#{@attribute}=", JsonWrapper.decrypt(record.send("#{@attribute}")))
  end

  def self.encrypt(value)
    value.to_json
  end

  def self.decrypt(value)
    JSON.parse(value) rescue value
  end
end

3)添加模型回调:

#app/models/user.rb

class User < ActiveRecord::Base
    before_save      JsonWrapper.new( :name )
    after_save       JsonWrapper.new( :name )

    def after_find
      self.name = JsonWrapper.decrypt self.name
    end
end

4)测试它!

User.create :name => {"a"=>"b", "c"=>["d", "e"]}

PS:

它不是很干,但我尽力了。如果有人可以修复模型after_findUser那就太好了。

于 2010-01-17T08:18:27.950 回答
8

我的要求在这个阶段不需要大量的代码重用,所以我的提炼代码是上述答案的变体:

  require "json/ext"

  before_save :json_serialize  
  after_save  :json_deserialize


  def json_serialize    
    self.options = self.options.to_json
  end

  def json_deserialize    
    self.options = JSON.parse(options)
  end

  def after_find 
    json_deserialize        
  end  

干杯,最后很容易!

于 2010-01-19T00:40:02.280 回答
3

serialize :attr, JSONusing方法的composed_of工作原理如下:

  composed_of :auth,
              :class_name => 'ActiveSupport::JSON',
              :mapping => %w(url to_json),
              :constructor => Proc.new { |url| ActiveSupport::JSON.decode(url) }

其中 url 是要使用 json 序列化的属性,而 auth 是模型上可用的新方法,它将 json 格式的值保存到 url 属性中。(尚未完全测试,但似乎正在工作)

于 2011-05-22T12:23:15.377 回答
3

我编写了自己的 YAML 编码器,它采用默认值。这是课程:

class JSONColumn
  def initialize(default={})
    @default = default
  end

  # this might be the database default and we should plan for empty strings or nils
  def load(s)
    s.present? ? JSON.load(s) : @default.clone
  end

  # this should only be nil or an object that serializes to JSON (like a hash or array)
  def dump(o)
    JSON.dump(o || @default)
  end
end

由于loaddump是实例方法,它需要一个实例作为模型定义中的第二个参数传递serialize。这是一个例子:

class Person < ActiveRecord::Base
  validate :name, :pets, :presence => true
  serialize :pets, JSONColumn.new([])
end

我尝试创建一个新实例、加载一个实例并将一个实例转储到 IRB 中,这一切似乎都正常工作。我也写了一篇关于它的博客文章。

于 2011-11-07T22:01:48.093 回答
1

一个更简单的解决方案是按照Michael Rykov 的这篇博客文章composed_of中的描述使用。我喜欢这个解决方案,因为它需要使用更少的回调。

这是它的要点:

composed_of :settings, :class_name => 'Settings', :mapping => %w(settings to_json),
                       :constructor => Settings.method(:from_json),
                       :converter   => Settings.method(:from_json)

after_validation do |u|
  u.settings = u.settings if u.settings.dirty? # Force to serialize
end
于 2011-01-28T06:58:13.353 回答
0

Aleran,你在 Rails 3 中使用过这种方法吗?我遇到了同样的问题,当我遇到 Michael Rykov 的这篇文章时,我正朝着连载的方向前进,但是不可能在他的博客上发表评论,或者至少在那篇文章上是不可能的。据我了解,他说您不需要定义设置类,但是当我尝试这样做时,它一直告诉我设置未定义。所以我只是想知道你是否使用过它,还有什么应该描述的?谢谢。

于 2011-04-15T14:41:29.220 回答