代码之家  ›  专栏  ›  技术社区  ›  Toby Hede

使用json而不是yaml进行ActiveRecord序列化

  •  54
  • Toby Hede  · 技术社区  · 16 年前

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

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

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

    8 回复  |  直到 9 年前
        1
  •  156
  •   Justas L.    14 年前

    在Rails 3.1中,您可以

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

    希望有所帮助

        2
  •  58
  •   nocache    14 年前

    在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
    

    希望这有帮助。

    参考文献:

    定义 串行化 : 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

        3
  •  11
  •   Kyle Heironimus    13 年前

    更新

    有关更合适的Rails答案,请参阅下面的MID高评级答案>=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_find 在里面 User 模特儿,会很棒的。

        4
  •  8
  •   Toby Hede    15 年前

    我的要求在这个阶段不需要很多代码重复使用,所以我提取的代码是上述答案的变体:

      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  
    

    干杯,最后很容易!

        5
  •  3
  •   Benjamin Atkin    13 年前

    我写了自己的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
    

    自从 load dump 是实例方法,它要求将实例作为第二个参数传递给 serialize 在模型定义中。下面是一个例子:

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

    我尝试创建一个新的实例,加载一个实例,并在IRB中转储一个实例,这一切似乎都正常工作。我写了一篇 blog post 关于它,也。

        6
  •  3
  •   bbonamin    13 年前

    这个 serialize :attr, JSON 使用 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属性中。(尚未完全测试,但似乎有效)

        7
  •  1
  •   Aleran    14 年前

    一个简单的解决方案是 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
    
        8
  •  0
  •   Andrew Lank    14 年前

    Aleran,你在Rails 3上使用过这个方法吗?我也遇到了同样的问题,当我遇到迈克尔·里科夫的这篇文章时,我正朝着连载的方向前进,但是在他的博客上发表评论是不可能的,至少在那篇文章上是这样。据我所知,他说您不需要定义设置类,但是当我尝试这个时,它总是告诉我设置没有定义。所以我只是想知道你是否使用过它,还有什么应该被描述的?谢谢。