如何在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中!