ActiveRecord serialize using JSON instead of YAML

46,224

Solution 1

In Rails 3.1 you can just

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

Hope that helps

Solution 2

In Rails 3.1 you can use custom coders with 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

Hope this helps.

References:

Definition of serialize: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/base.rb#L556

The default YAML coder that ships with rails: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/coders/yaml_column.rb

And this is where the call to the load happens: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/attribute_methods/read.rb#L132

Solution 3

Update

See mid's high rated answer below for a much more appropriate Rails >= 3.1 answer. This is a great answer for Rails < 3.1.

Probably this is what you're looking for.

Form.find(:first).to_json

Update

1) Install 'json' gem:

gem install json

2) Create JsonWrapper class

# 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) Add model callbacks:

#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) Test it!

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

PS:

It's not quite DRY, but I did my best. If anyone can fix after_find in User model, it'll be great.

Solution 4

My requirements didn't need a lot of code re-use at this stage, so my distilled code is a variation on the above answer:

  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  

Cheers, quite easy in the end!

Solution 5

The serialize :attr, JSON using composed_of method works like this:

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

where url is the attribute to be serialized using json and auth is the new method available on your model that saves its value in json format to the url attribute. (not fully tested yet but seems to be working)

Share:
46,224
SarahK
Author by

SarahK

Hands-on technical leader and developer with 20 years of experience in technology. Currently into Rust and nim. polyglot programmer. cloud architecture. scaling, distributed systems, &amp; databases. typescript | node.js | javascript | ruby | go | etc. many buzzwords. enthusiastic drummer. aspirational powerlifter. occasionally hilarious (citation needed).

Updated on May 17, 2020

Comments

  • SarahK
    SarahK almost 4 years

    I have a model that uses a serialized column:

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

    Is there a way to make this serialization use JSON instead of YAML?