跳至內容 跳至搜尋
方法
S

實例公開方法

serialize(attr_name, coder: nil, type: Object, yaml: {}, **options)

如果您有一個屬性需要以序列化物件的形式儲存到資料庫,並透過反序列化檢索為相同的物件,則使用此方法指定該屬性的名稱,序列化將會自動處理。

序列化格式可以是 YAML、JSON 或任何使用自定義編碼器類別的自定義格式。

請記住,資料庫適配器會為您處理某些序列化任務。例如:PostgreSQL 中的 jsonjsonb 類型將在 JSON 物件/陣列語法和 Ruby HashArray 物件之間透明地轉換。在這種情況下,無需使用 serialize

對於更複雜的情況,例如與您的應用程式網域物件之間的轉換,請考慮使用 ActiveRecord::Attributes API。

參數

  • attr_name - 要序列化的屬性名稱。

  • coder 要使用的序列化器實作,例如 JSON

    • 屬性值將使用編碼器的 dump(value) 方法進行序列化,並使用編碼器的 load(string) 方法進行反序列化。 dump 方法可以返回 nil 將值序列化為 NULL

  • type - 可選。序列化物件的類型應該是什麼。

  • yaml - 可選。Yaml 特定的選項。允許的設定是

    • :permitted_classes - 允許的類別的 Array

    • :unsafe_load - 不安全地載入 YAML Blob,允許 YAML 載入任何類別。

選項

  • :default - 未提供值時使用的預設值。如果未傳遞此選項,則將使用先前的預設值(如果有的話)。否則,預設值將為 nil

選擇序列化器

雖然可以使用任何序列化格式,但在使用之前建議仔細評估序列化器的屬性,因為稍後遷移到其他格式可能會很困難。

避免接受任意類型

在欄位中序列化數據時,強烈建議確保只序列化預期的類型。例如,像 MarshalYAML 之類的一些序列化器能夠序列化幾乎任何 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
# 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