Ruby YAML解析器通过传递构造函数
我正在从一个应用程序中获取YAML文件的输入,将它们解析成对象,让他们做他们的事情.我现在唯一的问题是,YAML解析器似乎忽略了对象“initialize”方法.我正在指望构造函数来填充YAML文件缺少默认值的任何实例变量,并将一些东西存储在类变量中.这是一个例子:
class Test @@counter = 0 def initialize(a,b) @a = a @b = b @a = 29 if @b == 3 @@counter += 1 end def self.how_many p @@counter end attr_accessor :a,:b end require 'YAML' a = Test.new(2,3) s = a.to_yaml puts s b = YAML::load(s) puts b.a puts b.b Test.how_many puts "" c = Test.new(4,4) c.b = 3 t = c.to_yaml puts t d = YAML::load(t) puts d.a puts d.b Test.how_many 我会预期以上输出: --- !ruby/object:Test a: 29 b: 3 29 3 2 --- !ruby/object:Test a: 4 b: 3 29 3 4 相反,我得到: --- !ruby/object:Test a: 29 b: 3 29 3 1 --- !ruby/object:Test a: 4 b: 3 4 3 2 我不明白如何使这些对象不使用它们定义的初始化方法.我也想知道是否有任何强制解析器使用initialize方法. 解决方法
从Yaml反序列化一个对象不使用initialize方法,因为一般来说,对象实例变量(这是默认的Yaml序列化存储)和初始化参数之间没有对应关系.
例如,考虑一个初始化的对象,看起来像这样(没有其他实例变量): def initialize(param_one,param_two) @a_variable = some_calculation(param_one,param_two) end 现在当一个实例被反序列化时,Yaml处理器的值为@a_variable,但是initialize方法需要两个参数,所以它不能调用它.即使实例变量的数量与初始化参数的数量相匹配,并不一定是它们对应的情况,即使处理器不知道它们被传递到初始化的顺序. 将Ruby对象序列化和反序列化为Yaml的默认过程是在序列化期间写出所有实例变量(使用其名称),然后在反序列化时分配类的新实例,并在此新实例上简单设置相同的实例变量. 当然有时你需要更多的控制这个过程.如果您使用的是Psych Yaml处理器(这是Ruby 1.9.3中的默认设置),那么您应该适当地实现encode_with(用于序列化)或init_with(用于反序列化)方法. 为了序列化,Psych将调用一个对象的encode_with方法,如果它存在,传递一个 对于反序列化,Psych会调用init_with方法,如果它存在于您的对象上,而不是使用上述默认过程,再次传递一个编码器对象.这一次,编码器将包含关于Yaml中对象表示的信息. 请注意,您不需要同时提供这两种方法,如果需要,您可以提供任何一种方法.如果同时提供这两个,那么在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时,它会看起来像这样,没有计算的值: --- !ruby/object:Foo first: 1 second: 2 当您将此Yaml加载到Ruby中时,创建的对象将设置@calculated实例变量. 如果你希望你可以在init_with内调用initialize,但是我认为在初始化一个新类的实例之前要清楚一点,并且从Yaml反序列化一个现有的实例.我会建议将常用逻辑提取到可以从两个方面调用的方法, (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |