从 Yaml 反序列化对象不使用该initialize
方法,因为通常在对象的实例变量(这是默认 Yaml 序列化存储的内容)和initialize
.
作为一个例子,考虑一个initialize
看起来像这样的对象(没有其他实例变量):
def initialize(param_one, param_two)
@a_variable = some_calculation(param_one, param_two)
end
现在当反序列化 this 的一个实例时,Yaml 处理器有一个值 for @a_variable
,但是该initialize
方法需要两个参数,所以它不能调用它。即使实例变量的数量与参数的数量相匹配,initialize
它们也不一定是对应的,即使它们对应,处理器也不知道它们应该传递给的顺序initialize
。
将 Ruby 对象序列化和反序列化到 Yaml 的默认过程是在序列化期间写出所有实例变量(及其名称),然后在反序列化时分配该类的新实例并在此新实例上简单地设置相同的实例变量。
当然,有时您需要更多地控制此过程。如果您使用的是 Psych Yaml 处理器(这是 Ruby 1.9.3 中的默认值),那么您应该适当地实现encode_with
(用于序列化)或或init_with
(用于反序列化)方法。
对于序列化,如果对象存在,Psych 将调用对象的encode_with
方法,并传递一个coder
object。这个对象允许你指定对象应该如何在 Yaml 中表示——通常你只是把它当作一个哈希。
对于反序列化,如果该方法存在于您的对象上,Psych 将调用该init_with
方法,而不是使用上述默认过程,再次传递一个coder
对象。这次coder
将包含有关 Yaml 中对象表示的信息。
请注意,您不需要同时提供这两种方法,如果需要,您可以只提供其中一种。如果两者都提供,则coder
传入的对象本质上与该方法运行后init_with
传递给的对象相同。encode_with
举个例子,考虑一个对象,它有一些实例变量是从其他变量中计算出来的(可能是为了避免大量计算的优化),但不应该序列化到 Yaml。
class Foo
def initialize(first, second)
@first = first
@second = second
@calculated = expensive_calculation(@first, @second)
end
def encode_with(coder)
# @calculated shouldn’t be serialized, so we just add the other two.
# We could provide different names to use in the Yaml here if we
# wanted (as long as the same names are used in init_with).
coder['first'] = @first
coder['second'] = @second
end
def init_with(coder)
# The Yaml only contains values for @first and @second, we need to
# recalculate @calculated so the object is valid.
@first = coder['first']
@second = coder['second']
@calculated = expensive_calculation(@first, @second)
end
# The expensive calculation
def expensive_calculation(a, b)
...
end
end
当你将这个类的一个实例转储到 Yaml 时,它看起来像这样,没有calculated
值:
--- !ruby/object:Foo
first: 1
second: 2
当您将此 Yaml 加载回 Ruby 时,创建的对象将@calculated
设置实例变量。
如果你愿意,你可以initialize
从inside调用init_with
,但我认为在初始化类的新实例和从 Yaml反序列化现有实例之间保持清晰的分离会更好。我建议将通用逻辑提取到可以从两者调用的方法中,