模組 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
或其他序列化格式,它只能載入簡單的「原始」類型,例如 String
、Array
、Hash
等。絕不允許使用者輸入指定要反序列化成任意類型。
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
次要版本
公開類別方法
序列化 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
無法傾印下列物件
-
匿名類別/模組。
-
與系統相關的物件(例如:
Dir
、File::Stat
、IO
、File
、Socket
等) -
MatchData
、Data
、Method
、UnboundMethod
、Proc
、Thread
、ThreadGroup
、Continuation
的實例 -
定義單例方法的物件
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); }
傳回將 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