- S
实例公共方法
serialize(attr_name, class_name_or_coder = nil, coder: nil, type: Object, yaml: {}, **options) 链接
如果您有一个属性需要作为序列化对象保存到数据库中,并通过反序列化为同一对象进行检索,那么使用此方法指定该属性的名称,序列化将自动处理。
序列化格式可以是 YAML、JSON 或使用自定义编码器类的任何自定义格式。
请记住,数据库适配器会为您处理某些序列化任务。例如:PostgreSQL 中的 json
和 jsonb
类型将在 JSON 对象/数组语法和 Ruby Hash
或 Array
对象之间透明转换。在这种情况下,无需使用 serialize
。
对于更复杂的情况,例如转换到或从您的应用程序域对象,请考虑使用 ActiveRecord::Attributes
API。
参数
-
attr_name
- 要序列化的属性的名称。 -
coder
要使用的序列化器实现,例如JSON
。-
属性值将使用编码器的
dump(value)
方法进行序列化,并将使用编码器的load(string)
方法进行反序列化。dump
方法可能会返回nil
以将值序列化为NULL
。
-
-
type
- 可选。序列化对象的类型应是什么。-
尝试序列化另一种类型将引发
ActiveRecord::SerializationTypeMismatch
错误。 -
如果列为
NULL
或从新记录开始,则默认值将设置为type.new
-
-
yaml
- 可选。Yaml 特定选项。允许的配置是-
:permitted_classes
- 允许的类的Array
。 -
:unsafe_load
- 不安全地加载 YAML blob,允许 YAML 加载任何类。
-
选项
-
:default
- 在未提供值时使用的默认值。如果未传递此选项,将使用前一个默认值(如果存在)。否则,默认值为nil
。
选择序列化器
虽然可以使用任何序列化格式,但在使用前建议仔细评估序列化器的属性,因为以后迁移到另一种格式可能会很困难。
避免接受任意类型
在列中序列化数据时,强烈建议确保仅序列化预期类型。例如,一些序列化器(如Marshal
或YAML
)能够序列化几乎任何 Ruby 对象。
这可能导致序列化意外类型,并且重要的是,只要某些数据库记录仍然包含这些序列化类型,类型序列化就保持向前和向后兼容。
class Address
def initialize(line, city, country)
@line, @city, @country = line, city, country
end
end
在上面的示例中,如果任何Address
属性被重命名,在更改之前持久化的实例将使用旧属性加载。当序列化的类型来自不希望以这种方式序列化的依赖项并且可能在不通知的情况下更改其内部表示时,此问题甚至更糟。
因此,强烈建议将这些对象转换为序列化格式的基本类型,例如
class Address
attr_reader :line, :city, :country
def self.load(payload)
data = YAML.safe_load(payload)
new(data["line"], data["city"], data["country"])
end
def self.dump(address)
YAML.safe_dump(
"line" => address.line,
"city" => address.city,
"country" => address.country,
)
end
def initialize(line, city, country)
@line, @city, @country = line, city, country
end
end
class User < ActiveRecord::Base
serialize :address, coder: Address
end
此模式允许更慎重地处理序列化的内容,并以向后兼容的方式演化格式。
确保序列化稳定性
一些序列化方法可能会接受它们不支持的一些类型,方法是将它们静默地转换为其他类型。当数据被反序列化时,这可能会导致错误。
例如,标准库中提供的JSON
序列化器会将不受支持的类型静默地转换为String
>> JSON.parse(JSON.dump(Struct.new(:foo)))
=> "#<Class:0x000000013090b4c0>"
示例
使用 YAML 序列化preferences
属性
class User < ActiveRecord::Base
serialize :preferences, coder: YAML
end
使用 JSON 序列化preferences
属性
class User < ActiveRecord::Base
serialize :preferences, coder: JSON
end
使用 YAML 序列化preferences
Hash
class User < ActiveRecord::Base
serialize :preferences, type: Hash, coder: YAML
end
将preferences
序列化为 YAML,允许选择类
class User < ActiveRecord::Base
serialize :preferences, coder: YAML, yaml: { permitted_classes: [Symbol, Time] }
end
使用自定义编码器序列化preferences
属性
class Rot13JSON
def self.rot13(string)
string.tr("a-zA-Z", "n-za-mN-ZA-M")
end
# Serializes an attribute value to a string that will be stored in the database.
def self.dump(value)
rot13(ActiveSupport::JSON.dump(value))
end
# Deserializes a string from the database to an attribute value.
def self.load(string)
ActiveSupport::JSON.load(rot13(string))
end
end
class User < ActiveRecord::Base
serialize :preferences, coder: Rot13JSON
end
来源:显示 | 在 GitHub 上
# File activerecord/lib/active_record/attribute_methods/serialization.rb, line 183 def serialize(attr_name, class_name_or_coder = nil, coder: nil, type: Object, yaml: {}, **options) unless class_name_or_coder.nil? if class_name_or_coder == ::JSON || [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) } ActiveRecord.deprecator.warn(<<~MSG) Passing the coder as positional argument is deprecated and will be removed in Rails 7.2. Please pass the coder as a keyword argument: serialize #{attr_name.inspect}, coder: #{class_name_or_coder} MSG coder = class_name_or_coder else ActiveRecord.deprecator.warn(<<~MSG) Passing the class as positional argument is deprecated and will be removed in Rails 7.2. Please pass the class as a keyword argument: serialize #{attr_name.inspect}, type: #{class_name_or_coder.name} MSG type = class_name_or_coder end end coder ||= default_column_serializer unless coder raise ArgumentError, <<~MSG.squish missing keyword: :coder If no default coder is configured, a coder must be provided to `serialize`. MSG end column_serializer = build_column_serializer(attr_name, coder, type, yaml) attribute(attr_name, **options) do |cast_type| if type_incompatible_with_serialize?(cast_type, coder, type) raise ColumnNotSerializableError.new(attr_name, cast_type) end cast_type = cast_type.subtype if Type::Serialized === cast_type Type::Serialized.new(cast_type, column_serializer) end end