module Marshal

Marshal 库可以将 Ruby 对象集合转换为字节流,允许它们存储在当前活动脚本之外。这些数据随后可以被读取并重新构造原始对象。

Marshal 数据在存储对象信息的同时还存储了主版本号和次版本号。在正常使用情况下,Marshal 只能加载用相同主版本号且次版本号相等或更低的版本写入的数据。如果 Ruby 的“verbose”标志已设置(通常使用 -d、-v、-w 或 –verbose),则主版本号和次版本号必须完全匹配。 Marshal 版本控制独立于 Ruby 的版本号。您可以通过读取 Marshal 数据的头两个字节来提取版本号。

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

转储对象时将调用 `marshal_dump` 方法。`marshal_dump` 必须返回一个包含 `marshal_load` 重构对象所需信息的 `result`。`result` 可以是任何对象。

当加载使用 `marshal_dump` 转储的对象时,对象首先被分配,然后调用 `marshal_load`,并传入 `marshal_dump` 的 `result`。`marshal_load` 必须从 `result` 中的信息重新创建对象。

示例

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`。

转储对象时,将调用实例方法 `_dump`,并传入一个 Integer,该整数指示要转储的最大对象深度(值为 -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` 进行解析,以处理复杂对象。