實例公開方法
serialize(attr_name, 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, coder: nil, type: Object, yaml: {}, **options) 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) decorate_attributes([attr_name]) do |attr_name, 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