2010-01-17 3 views
54

У меня есть модель, которая использует упорядоченный столбец:ActiveRecord сериализацию с использованием JSON вместо YAML

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

Есть ли способ сделать это сериализации использовать JSON вместо YAML?

+0

Я надеялся, что будет какая-то «магия» или плагин, но решение довольно прямолинейно и работает очень красиво. –

+0

toby, можете ли вы изменить принятый ответ на этот вопрос? http://stackoverflow.com/a/6971678/190135 см. http://meta.stackexchange.com/questions/120568/is-it-possible-to-change-the-chosen-answer – AlexChaffee

ответ

155

В Rails 3.1 вы можете просто

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

Надежда, что помогает

+1

Я думаю, что это потому, что '[: dump,: load] .all {| s | :: JSON.respond_to? s} ', как и по умолчанию' YAMLColumn'. В контроллере 'JSON == :: JSON' и' JSON! = :: ActiveSupport :: JSON'. –

+1

Это работало через Rails 3.2.1, но остановилось с 3.2.2: см. Https://github.com/rails/rails/issues/5797. Надеюсь, это исправится, потому что это очень удобно! – gmcnaughton

+2

Это исправлено в 3.2.5 –

11

Update

высокого рейтинг ответ знакомства середины в ниже гораздо более подходящем Rails> = 3.1 ответа. Это отличный ответ для Rails < 3.1.

Возможно, это то, что вы ищете.

Form.find(:first).to_json 

Update

1) Установить '' gem JSON:

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:

Это не совсем DRY, но я сделал все возможное. Если кто-то может исправить after_find в модели User, это будет здорово.

+1

Вы не понимаете, что , Он хочет: атрибут options должен быть сериализован в json-данные, а не в yaml. – Eimantas

+0

Ja, используя сериализацию, значения в поле параметров сохраняются в базе данных как сериализованное значение YAML. Я хочу, чтобы это был JSON. –

+0

Обновлен ответ. –

8

Мои требования не нужно много кода повторного использования на данном этапе, так что мой дистиллированная код представляет собой вариацию на ответе выше:

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, довольно легко, в конце концов!

+0

Это будет работать только с определенными типами данных. Например, 'input = 'foo'; serialized = input.to_json; JSON.parse (сериализованный) ' # => JSON :: ParserError: 757: неожиданный токен в '" foo "' из /Users/username/.rbenv/versions/2.2.2/lib/ruby/gems/2.2 .0/gems/json-1.8.3/lib/json/common.rb: 155: in 'parse ' https://makandracards.com/makandra/15611-how-to-fix-unexpected-token-error -for-json-parse Хорошим обходным путем для этого является использование библиотеки маршала Ruby. Это быстрее и надежнее, чем JSON или YAML. –

1

Простым решением является использование 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 
+0

К сожалению, created_of устарел. http://blog.plataformatec.com.br/2012/06/about-the-composed_of-removal/ рекомендует вместо этого использовать собственный сериализатор. –

0

Алеранского, вы использовали этот метод с Rails 3? У меня есть одна и та же проблема, и я шел к сериализации, когда я столкнулся с этим сообщением Майклом Рыковым, но комментировать его блог невозможно, или, по крайней мере, на этом посту. Насколько я понимаю, он говорит, что вам не нужно определять класс настроек, однако, когда я пытаюсь это сделать, он продолжает говорить мне, что параметр Setting не определен. Поэтому мне просто интересно, использовали ли вы его и что еще нужно было описать? Благодарю.

+0

Конечно, вам нужен класс Setting. Но вы, вероятно, можете напрямую работать над классом JSON. : constructor => JSON.method (: parse),: converter => JSON.method (: parse),: mapping =>% w (my_attribute to_json). Однако не пытались это сделать. – balu

58

В 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

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

+1

Хотя он не был включен в Rails, во время написания здесь был добавлен JSONColumn Coder: https://gist.github.com/txus/rails/blob/1a2ce8e56c4313a7466c2b0b08a037e44d033f6c/activerecord/lib/active_record/coders /json_column.rb - подробности запроса на вытягивание приведены здесь: https://gist.github.com/rails/rails/pull/196. Надеюсь, это поможет любому, кто хочет использовать serialize + JSON. –

3

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. (Еще не полностью протестированы, но, кажется, работает)

3

я написал свой собственный 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.

+0

Я действительно новичок в Rails. Где бы я поместил класс JSONColumn? Где я могу включить его? Благодаря! –

+0

Есть много способов сделать это, но вот один из способов: вы можете поместить его в 'lib/json_column.rb' и добавить' require 'json_column'' в 'config/environment.rb'. –

+0

Спасибо за это. Приятно, что вы можете установить значение по умолчанию. Я использовал это для создания столбца, который действует как набор (неупорядоченный массив): serialize: something, JsonSerializer.new (Set.new) –

 Смежные вопросы

  • Нет связанных вопросов^_^