ActiveRecord serializza usando JSON invece di YAML

Ho un modello che utilizza una colonna serializzata:

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

C’è un modo per far sì che questa serializzazione usi JSON invece di YAML?

In Rails 3.1 puoi solo

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

Spero possa aiutare

In Rails 3.1 è ansible utilizzare i codificatori personalizzati 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 

Spero che questo ti aiuti.

Riferimenti:

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

Il codificatore YAML predefinito fornito con le guide: https://github.com/rails/rails/blob/master/activerecord/lib/active_record/coders/yaml_column.rb

Ed è qui che si verifica la chiamata al load : https://github.com/rails/rails/blob/master/activerecord/lib/active_record/attribute_methods/read.rb#L132

Aggiornare

Vedi la risposta più votata di mid di seguito per una Rails molto più appropriata> = 3.1 risposta. Questa è un’ottima risposta per Rails <3.1.

Probabilmente questo è quello che stai cercando.

 Form.find(:first).to_json 

Aggiornare

1) Installa gem ‘json’:

 gem install json 

2) Crea class 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) Aggiungi callback del modello:

 #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) Provalo!

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

PS:

Non è abbastanza ASCIUTTO, ma ho fatto del mio meglio. Se qualcuno può risolvere after_find nel modello User , sarà fantastico.

I miei requisiti non richiedevano molto riutilizzo del codice in questa fase, quindi il mio codice distillato è una variazione della risposta di cui sopra:

  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 

Saluti, abbastanza facile alla fine!

Ho scritto il mio codificatore YAML, che ha un valore predefinito. Ecco la class:

 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 

Poiché load e dump sono metodi di istanza, è necessario che un’istanza venga passata come secondo argomento da serialize nella definizione del modello. Ecco un esempio di questo:

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

Ho provato a creare una nuova istanza, a caricare un’istanza ea scaricare un’istanza in IRB e tutto sembrava funzionare correttamente. Ho scritto anche un post sul blog .

Il serialize :attr, JSON usa il metodo composed_of funziona così:

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

dove url è l’attributo da serializzare usando json e auth è il nuovo metodo disponibile sul tuo modello che salva il suo valore in formato json nell’attributo url. (non ancora completamente testato ma sembra funzionare)

Una soluzione più semplice è l’uso di composed_of come descritto in questo post del blog di Michael Rykov . Mi piace questa soluzione perché richiede l’uso di un minor numero di callback.

Ecco il succo di ciò:

 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, hai usato questo metodo con Rails 3? In qualche modo ho avuto lo stesso problema e mi stavo dirigendo verso la serializzazione quando mi sono imbattuto in questo post di Michael Rykov, ma commentare il suo blog non è ansible, o almeno su quel post. Per quanto ho capito, sta dicendo che non è necessario definire la class Settings, tuttavia quando provo questo continua a dirmi che Setting non è definito. Quindi mi stavo chiedendo se l’hai usato e quale altro avrebbe dovuto essere descritto? Grazie.