如何在Psych中反序列化类?
如何在Psych中反序列化以返回现有对象,例如类对象?
要做类的序列化,我可以做
require "psych" class Class yaml_tag 'class' def encode_with coder coder.represent_scalar 'class', name end end yaml_string = Psych.dump(String) # => "--- ! String\n...\n"
但如果我尝试使用Psych.load
,我会得到一个匿名类,而不是String类。
正常的反序列化方法是Object#init_with(coder)
,但这只会改变现有匿名类的状态,而我想要String类。
Psych::Visitors::ToRuby#visit_Psych_Nodes_Scalar(o)
有一些情况,而不是用init_with
修改现有对象,它们确保首先创建正确的对象(例如,调用Complex(o.value)
来反序列化一个复合体数字),但我不认为我应该monkeypatching该方法。
我注定要使用低水平或中等水平的发射,或者我错过了什么?
背景
我将描述该项目,它为什么需要类,以及它为什么需要(反)序列化。
项目
Small Eigen Collider旨在为Ruby创建随机任务。 最初的目的是看看Ruby的不同实现(例如,Rubinius和JRuby)在给定相同的随机任务时是否返回相同的结果,但我发现它对检测Rubinius和YARV的段错误也很有用。
每项任务由以下内容组成:
receiver.send(method_name, *parameters, &block)
其中receiver
是随机选择的对象, method_name
是随机选择的方法的名称, *parameters
是随机选择的对象的数组。 &block
不是很随机 – 它基本上等同于{|o| o.inspect}
{|o| o.inspect}
。
例如,如果接收器是“a”,method_name是:casecmp,参数是[“b”],那么你将调用
"a".send(:casecmp, "b") {|x| x.inspect}
相当于(因为块无关紧要)
"a".casecmp("b")
Small Eigen Collider运行此代码,并记录这些输入以及返回值。 在这个例子中,Ruby的大多数实现返回-1,但在一个阶段,Rubinius返回+1。 (我把它作为一个错误提交https://github.com/evanphx/rubinius/issues/518并且Rubinius维护者修复了这个bug)
为什么需要上课
我希望能够在我的Small Eigen Collider中使用类对象。 通常,它们是接收器,但它们也可以是参数之一。
例如,我发现了一种分段YARV的方法就是这样做
Thread.kill(nil)
在这种情况下,receiver是类对象Thread,参数是[nil]。 (错误报告: http : //redmine.ruby-lang.org/issues/show/4367 )
为什么需要(反)序列化
由于几个原因,小特征对撞机需要序列化。
一个是每次使用随机数生成器生成一系列随机任务是不切实际的。 JRuby有一个不同的内置随机数生成器,所以即使给出相同的PRNG种子,它也会为YARV提供不同的任务。 相反,我做的是创建一个随机任务列表(第一次运行ruby bin / small_eigen_collider),让初始运行序列化任务列表到tasks.yml,然后运行该程序(使用不同的Ruby实现)读入tasks.yml文件以获取任务列表。
我需要序列化的另一个原因是我希望能够编辑任务列表。 如果我有一长串导致分段错误的任务,我想将列表减少到导致分段错误所需的最小值。 例如,有以下错误https://github.com/evanphx/rubinius/issues/643 ,
ObjectSpace.undefine_finalizer(:symbol)
本身不会导致分段错误,也不会
Symbol.all_symbols.inspect
但如果你把两者放在一起,那就确实如此。 但是我从数千个任务开始,并且需要将其削减到这两个任务。
在这种情况下,返回现有类对象的反序列化是否有意义,或者您认为有更好的方法吗?
我目前研究的现状:
要使您所需的行为正常工作,您可以使用上面提到的解决方法。
这里格式很好的代码示例:
string_yaml = Psych.dump(Marshal.dump(String)) # => "--- ! \"\\x04\\bc\\vString\"\n" string_class = Marshal.load(Psych.load(string_yaml)) # => String
修改Class的hack可能永远不会起作用,因为在mental / yaml中没有实现真正的类处理。
你可以把这个repo tenderlove / psych ,这是独立的lib。
(gem: 心理 – 加载它,使用: gem 'psych'; require 'psych'
并与Psych::VERSION
进行检查)
正如您在第249-251行中所看到的那样,处理具有匿名类Class的对象不会被处理。
我没有对class级进行monkeypatching,而是建议您通过扩展此类处理来为Psych lib做出贡献。
所以在我看来,最终的yaml结果应该是这样的: "--- !ruby/class String"
经过一夜思考,我可以说,这个function真的很棒!
更新
找到一个似乎按预期方式工作的小解决方案:
代码要点: gist.github.com/1012130 (带描述性评论)
Psych维护者已经实现了类和模块的序列化和反序列化。 它现在在Ruby中!