Marshal

您可以使用该模块把Ruby对象写入文件(或字符串)保存起来,或者重新读取并生成该对象. 绝大部分类的实例都是可以保存的, 但有的类确实不行(后述).

下文中提到的"marshal data"是指Marshal.dump输出的字符串.

模块函数:

Marshal.dump(obj[,port][,limit])

obj递归地写入文件. 若对那些不能被写入文件的对象使用该函数时, 会引发TypeError异常. 下列对象就不能被写入文件.

另外,间接指向上述对象的那些对象也是不可保存的. 例如, Hash内部包含求默认值的块, 实际上,它是间接地指向Proc的.

p Marshal.dump(Hash.new {})
=> -:1:in `dump': cannot dump hash with default proc (TypeError)

可以向port指定一个IO(或其子类)的实例. 此时将返回port. 缺省时, dump会返回保存着对象的字符串.

若使用了limit时, 将只会保存limit层以内的关联对象(默认值为100层). 若将limit指定为负值时, 将不会进行层数检查.

您可以自由地定义marshal的运作细节, 详情请参考用户定义的Marshal.

Marshal.load(port[,proc])
Marshal.restore(port[,proc])

port读入marshal data后生成一个与原对象状态相同的对象. port可以是字符串或IO(或其子类)的实例.

若给出了过程对象proc时, 将以读入的对象作参数来调用该过程对象.

str = Marshal.dump(["a", 1, 10 ** 10, 1.0, :foo])
p Marshal.load(str, proc {|obj| p obj})

=> "a"
   1
   10000000000
   1.0
   :foo
   ["a", 1, 10000000000, 1.0, :foo]
   ["a", 1, 10000000000, 1.0, :foo]

常数:

MAJOR_VERSION ((<ruby 1.7 特性>))
MINOR_VERSION ((<ruby 1.7 特性>))

Marshal.dump输出的数据格式的版本号.

Marshal.load中, 若major version不同,或者读入了高版本的marshal data时, 会引发TypeError异常.

minor version版本较低的数据是可读的. 但若$VERBOSE = true时, 将出现警告消息.

可以使用下面的方法来获得数据的版本号.

obj = Object.new
major, minor = Marshal.dump(obj).unpack("cc")
p [major, minor]

用户定义的Marshal

Object#_dump(limit)
Class#_load(str)

使用Marshal.dump保存对象时, 若该对象中已经定义了`_dump'方法的话, 就直接使用该方法的返回值. `_dump'方法接受limit对递归操作的限制, 将对象变为字符串并返回结果.

若某类的实例中包含`_dump'方法的话, 则该类中必需包含一个名为`_load'的类方法, 它负责读回数据. `_load'在收到一个保存着对象的字符串之后, 必须将它还原为对象.

class Foo
  def initialize(obj)
    p "initialize() called"
    @foo = obj
  end
  def _dump(limit)
    Marshal.dump(@foo, limit)
  end
  def self._load(obj)
    Foo.new(Marshal.load(obj))
  end
end

p Marshal.load(Marshal.dump(Foo.new(['foo', 'bar'])))

=> "initialize() called"
   "initialize() called"
   #<Foo:0x4019eb88 @foo=["foo", "bar"]>

通常情况下, 实例变量的信息都被保存在marshal data中, 所以不必像上例那样去定义 _dump (但是,若定义了_dump的话, 实例变量的信息就不会被保存下来了). 若您想进行更加灵活的控制 ,或者扩展库中定义的类的实例需要保存除实例变量之外的更多信息时, 可以使用_dump/_load. (例如Time类就定义了_dump/_load)

Object#marshal_dump ((<ruby 1.8 特性>))
Object#marshal_load ((<ruby 1.8 特性>))

在使用Marshal.dump保存对象时, 若该对象中已定义了`marshal_dump'方法的话, 就直接使用该方法的返回值. marshal_dump被设计成一个可以返回任意对象的方法.

因此,在load这样的对象时, 就必需事先定义过一个名为`marshal_load'的方法. marshal_load接收marshal_dump返回值的拷贝. 此时marshal_load的self处于新生(刚被allocate的)状态. 另外, marshal_load的返回值将被忽略.

若该对象中同时包含 _dump 和 marshal_dump 方法时, 将使用 marshal_dump 方法.

class Foo
  def initialize
    p "initialize() called"
    @foo = ['foo', 'bar']
  end
  def marshal_dump
    @foo
  end
  def marshal_load(obj)
    @foo = obj
  end
end

p Marshal.load(Marshal.dump(Foo.new))

"initialize() called"
#<Foo:0x4019ed2c @foo=["foo", "bar"]>

通常情况下, 实例变量的信息会被保存在marshal data中, 所以不必像上例那样去定义 marshal_dump (但是,若定义了marshal_dump的话, 实例变量的信息就不会被保存下来了,所以必须使用marshal_dump/marshal_load来进行处理). 若您想进行更加灵活的控制 ,或者扩展库中定义的类的实例需要保存除实例变量之外的更多信息时, 可以使用marshal_dump/marshal_load.

而且, 若某对象中已经定义了marshal_dump/marshal_load的话, 即时其内部包含特殊方法, 也可以进行保存. (这并不是说特殊方法会被自动保存下来, 而是说您可以通过marshal_dump/marshal_load来进行相应的处理)