ActiveRecord serialize utilizando JSON en lugar de YAML

Tengo un modelo que usa una columna serializada:

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

¿Hay alguna manera de hacer que esta serialización use JSON en lugar de YAML?

En Rails 3.1 puede simplemente

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

Espero que ayude

En Rails 3.1 puede usar codificadores personalizados con 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 

Espero que esto ayude.

Referencias

Definición de serialize : https://github.com/rails/rails/blob/master/activerecord/lib/active_record/base.rb#L556

El codificador YAML predeterminado que se envía con Rails: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/coders/yaml_column.rb

Y aquí es donde sucede la llamada a la load : https://github.com/rails/rails/blob/master/activerecord/lib/active_record/attribute_methods/read.rb#L132

Actualizar

Vea la respuesta mejor calificada de mid a continuación para una respuesta Rails> = 3.1 mucho más apropiada. Esta es una gran respuesta para Rails <3.1.

Probablemente esto es lo que estás buscando.

 Form.find(:first).to_json 

Actualizar

1) Instalar la gem ‘json’:

 gem install json 

2) Crear la clase 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) Agregue devoluciones de llamada modelo:

 #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) ¡Pruébalo!

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

PD:

No es bastante SECO, pero hice mi mejor esfuerzo. Si alguien puede corregir after_find en el modelo de User , será genial.

Mis requisitos no necesitaban mucha reutilización de código en esta etapa, por lo que mi código destilado es una variación de la respuesta anterior:

  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 

Saludos, bastante fácil al final!

Escribí mi propio codificador YAML, que toma un valor predeterminado. Aquí está la clase:

 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 

Como load y dump son métodos de instancia, se requiere pasar una instancia como segundo argumento para serialize en la definición del modelo. Aquí hay un ejemplo de esto:

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

Traté de crear una nueva instancia, cargar una instancia y volcar una instancia en IRB, y todo parecía funcionar correctamente. Escribí una publicación en el blog sobre eso, también.

El método serialize :attr, JSON using composed_of funciona de la siguiente manera:

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

donde url es el atributo que se serializará usando json y auth es el nuevo método disponible en su modelo que guarda su valor en formato json en el atributo url. (Aún no se ha probado completamente pero parece estar funcionando)

Una solución más simple es usar composed_of como se describe en esta publicación de blog por Michael Rykov . Me gusta esta solución porque requiere el uso de menos devoluciones de llamada.

Aquí está la esencia de esto:

 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 

Aleran, ¿has usado este método con Rails 3? De alguna forma, tengo el mismo problema y me dirigía a la serialización cuando me encontré con esta publicación de Michael Rykov, pero comentar sobre su blog no es posible, o al menos en esa publicación. A mi entender, él está diciendo que no es necesario definir la clase de configuración, sin embargo, cuando bash esto, me sigue diciendo que la configuración no está definida. Entonces, me preguntaba si lo ha usado y qué más debería haberse descrito. Gracias.