模組 Marshal

編組函式庫會將 Ruby 物件集合轉換為位元組串流,讓它們可以儲存在目前作用中指令碼的外部。此資料稍後可以讀取,並重建原始物件。

編組資料會與物件資訊一起儲存主版本號碼和次要版本號碼。在正常使用下,編組只能載入使用相同主版本號碼和相等或較低次要版本號碼所寫入的資料。如果 Ruby 的「詳細」旗標已設定(通常使用 -d、-v、-w 或 –verbose),則主版本號碼和次要版本號碼必須完全相符。Marshal 版本控管與 Ruby 的版本號碼無關。您可以透過讀取編組資料的前兩個位元組來擷取版本。

str = Marshal.dump("thing")
RUBY_VERSION   #=> "1.9.0"
str[0].ord     #=> 4
str[1].ord     #=> 8

有些物件無法傾印:如果要傾印的物件包含繫結、程序或方法物件、IO 類別的執行個體,或單例物件,則會引發 TypeError

如果您的類別有特殊序列化需求(例如,您想要以特定格式序列化),或如果它包含否則無法序列化的物件,您可以實作自己的序列化策略。

有兩種方法可以做到這一點,您的物件可以定義 marshal_dump 和 marshal_load 或 _dump 和 _load。如果兩個都已定義,marshal_dump 將優先於 _dump。marshal_dump 可能會產生較小的 Marshal 字串。

安全性考量

根據設計,Marshal.load 可以將載入 Ruby 程序中的幾乎任何類別反序列化。在許多情況下,如果 Marshal 資料從不受信任的來源載入,這可能會導致遠端程式碼執行。

因此,Marshal.load 不適合作為一般用途的序列化格式,您絕不應該取消編組使用者提供的輸入或其他不受信任的資料。

如果您需要反序列化不受信任的資料,請使用 JSON 或其他序列化格式,它只能載入簡單的「原始」類型,例如 StringArrayHash 等。絕不允許使用者輸入指定要反序列化成任意類型。

marshal_dump 和 marshal_load

當傾印物件時,將呼叫 method marshal_dump。marshal_dump 必須傳回一個結果,其中包含 marshal_load 重建物件所需的資訊。結果可以是任何物件。

當載入使用 marshal_dump 傾印的物件時,會先配置物件,然後使用 marshal_dump 的結果呼叫 marshal_load。marshal_load 必須從結果中的資訊重新建立物件。

範例

class MyObj
  def initialize name, version, data
    @name    = name
    @version = version
    @data    = data
  end

  def marshal_dump
    [@name, @version]
  end

  def marshal_load array
    @name, @version = array
  end
end

_dump 和 _load

當您需要配置要自行還原的物件時,請使用 _dump 和 _load。

當傾印物件時,會使用 Integer 呼叫實例方法 _dump,它表示要傾印的物件最大深度(-1 的值表示您應該停用深度檢查)。_dump 必須傳回一個 String,其中包含重建物件所需的資訊。

類別方法 _load 應採用 String,並使用它傳回相同類別的物件。

範例

class MyObj
  def initialize name, version, data
    @name    = name
    @version = version
    @data    = data
  end

  def _dump level
    [@name, @version].join ':'
  end

  def self._load args
    new(*args.split(':'))
  end
end

由於 Marshal.dump 會輸出字串,因此您可以讓 _dump 傳回 Marshal 字串,而 _load 中的 Marshal.loaded 則用於複雜物件。

常數

MAJOR_VERSION

主要版本

MINOR_VERSION

次要版本

公開類別方法

dump( obj [, anIO] , limit=-1 ) → anIO 按一下以切換來源

序列化 obj 和所有後代物件。如果指定 anIO,序列化資料會寫入其中,否則資料會傳回為 String。如果指定 limit,子物件的遍歷將限制在該深度。如果 limit 為負值,將不執行深度檢查。

class Klass
  def initialize(str)
    @str = str
  end
  def say_hello
    @str
  end
end

(不產生輸出)

o = Klass.new("hello\n")
data = Marshal.dump(o)
obj = Marshal.load(data)
obj.say_hello  #=> "hello\n"

Marshal 無法傾印下列物件

static VALUE
marshal_dump(int argc, VALUE *argv, VALUE _)
{
    VALUE obj, port, a1, a2;
    int limit = -1;

    port = Qnil;
    rb_scan_args(argc, argv, "12", &obj, &a1, &a2);
    if (argc == 3) {
        if (!NIL_P(a2)) limit = NUM2INT(a2);
        if (NIL_P(a1)) io_needed();
        port = a1;
    }
    else if (argc == 2) {
        if (FIXNUM_P(a1)) limit = FIX2INT(a1);
        else if (NIL_P(a1)) io_needed();
        else port = a1;
    }
    return rb_marshal_dump_limited(obj, port, limit);
}
load(source, proc = nil, freeze: false) → obj 按一下以切換來源

傳回將 source 中的序列化資料轉換為 Ruby 物件(可能與關聯的從屬物件一起)的結果。source 可以是 IO 的執行個體,或回應 to_str 的物件。如果指定 proc,每個物件會在反序列化物件時傳遞給 proc。

切勿將不受信任的資料(包括使用者提供的輸入)傳遞給此方法。請參閱概觀以取得進一步的詳細資料。

如果傳遞 freeze: true 參數,反序列化的物件將會深度凍結。請注意,由於凍結字串會進行重複資料刪除,這可能會導致更有效率的記憶體使用率

serialized = Marshal.dump(['value1', 'value2', 'value1', 'value2'])

deserialized = Marshal.load(serialized)
deserialized.map(&:frozen?)
# => [false, false, false, false]
deserialized.map(&:object_id)
# => [1023900, 1023920, 1023940, 1023960] -- 4 different objects

deserialized = Marshal.load(serialized, freeze: true)
deserialized.map(&:frozen?)
# => [true, true, true, true]
deserialized.map(&:object_id)
# => [1039360, 1039380, 1039360, 1039380] -- only 2 different objects, object_ids repeating
# File marshal.rb, line 33
def self.load(source, proc = nil, freeze: false)
  Primitive.marshal_load(source, proc, freeze)
end
也別名為: restore
restore(source, proc = nil, freeze: false) → obj
別名為: load