ruby-on-rails ActiveRecord使用JSON而不是YAML进行序列化

lf5gs5x2  于 2022-11-19  发布在  Ruby
关注(0)|答案(8)|浏览(145)

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

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

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

mwecs4sa

mwecs4sa1#

在Rails 3.1中,您只需

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

希望能有所帮助

tquggr8v

tquggr8v2#

在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

希望这对你有帮助。
参考文献:
serialize的定义:https://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

6jjcrrmo

6jjcrrmo3#

更新

下面是mid给出的高评分答案,这是一个更适合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"]}

附言:

这不是很干,但我已经尽力了。如果有人能修复User模型中的after_find,这将是伟大的。

bvk5enib

bvk5enib4#

在这个阶段,我的需求不需要大量的代码重用,所以我的精简代码是上面答案的一个变体:

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

干杯,总算轻松了!

k7fdbhmy

k7fdbhmy5#

使用composed_of方法的serialize :attr, JSON的工作方式如下:

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属性中。(尚未完全测试,但似乎正在工作)

db2dz4w8

db2dz4w86#

我写了我自己的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中创建一个新示例,加载一个示例,转储一个示例,看起来一切都正常。我也写了一个blog post

aelbi1ox

aelbi1ox7#

一个更简单的解决方案是使用composed_of,如this blog post by Michael Rykov中所述。我喜欢这个解决方案,因为它需要使用更少的回调。
下面是它的要点:

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
xyhw6mcr

xyhw6mcr8#

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

相关问题