模块 Marshal

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

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

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

有些对象无法被 dump:如果要 dump 的对象包括绑定、过程或方法对象、IO 类的实例或单例对象,则会引发 TypeError

如果你的类有特殊的序列化需求(例如,如果你想以某种特定格式序列化),或者如果它包含其他无法序列化的对象,你可以实现自己的序列化策略。

有两种方法可以做到这一点,你的对象可以定义 marshal_dump 和 marshal_load 或 _dump 和 _load。如果两者都定义了,marshal_dump 将优先于 _dump。marshal_dump 可能会产生更小的 Marshal 字符串。

安全注意事项

根据设计,Marshal.load 可以反序列化几乎任何加载到 Ruby 进程中的类。在许多情况下,如果从不受信任的来源加载 Marshal 数据,这可能导致远程代码执行。

因此,Marshal.load 不适合作为通用的序列化格式,并且你永远不应该 unmarshal 用户提供的输入或其他不受信任的数据。

如果你需要反序列化不受信任的数据,请使用 JSON 或其他只能加载简单的“原始”类型的序列化格式,例如 StringArrayHash 等。永远不允许用户输入指定要反序列化为的任意类型。

marshal_dump 和 marshal_load

在 dump 对象时,将调用方法 marshal_dump。marshal_dump 必须返回一个结果,其中包含 marshal_load 重构对象所需的信息。结果可以是任何对象。

当加载使用 marshal_dump 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。

在 dump 对象时,将使用一个 Integer 调用实例方法 _dump,该 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.load 用于复杂对象。