ruby – 如何在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) # => "--- !<class> 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是
随机选择的方法的名称,*参数是一个数组
随机选择的对象. & block不是很随意 – 基本上就是这样
相当于{| o | o.inspect}.

例如,如果receiver是“a”,则method_name为:casecmp,和
参数是[“b”],然后你会打电话

"a".send(:casecmp, "b") {|x| x.inspect}

相当于(因为块无关紧要)

"a".casecmp("b")

小特征碰撞器运行此代码,并记录这些输入和
还有返回值.在这个例子中,Ruby的大多数实现
返回-1,但在一个阶段,Rubinius返回1.(我提交了这个
bug https://github.com/evanphx/rubinius/issues/518和Rubinius
维护者修复了这个bug)

为什么需要上课

我希望能够在我的Small Eigen Collider中使用类对象.
通常,他们将是接收者,但他们也可能是其中之一
参数.

例如,我发现一种分段YARV的方法就是这样做

Thread.kill(nil)

在这种情况下,receiver是类对象Thread,参数是
[零]. (错误报告:http://redmine.ruby-lang.org/issues/show/4367)

为什么需要(反)序列化

由于几个原因,小特征对撞机需要序列化.

一个是使用随机数生成器生成一系列
每次随机任务都不实用. JRuby有一个不同的内置
随机数生成器,所以即使给出相同的PRNG种子它也是
为YARV提供不同的任务.相反,我所做的是创建一个列表
随机任务一次(第一次运行ruby
bin / small_eigen_collider),让初始运行序列化列表
任务到tasks.yml,然后有后续运行的
程序(使用不同的Ruby实现)读入tasks.yml
文件以获取任务列表.

我需要序列化的另一个原因是我希望能够编辑
任务列表.如果我有很长的任务列表导致a
分段错误,我想将列表减少到最低要求
导致分段错误.例如,出现以下错误
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.

(宝石:psych – 加载它,使用:宝石’心理’;需要’心理’并与Psych :: VERSION进行检查)

正如您在line 249-251中所看到的那样,处理具有匿名类Class的对象不会被处理.

我没有对班级进行monkeypatching,而是建议您通过扩展此类处理来为Psych lib做贡献.

所以在我看来,最终的yaml结果应该是这样的:“—!ruby / class String”

经过一夜思考,我可以说,这个功能真的很棒!

更新

找到一个似乎按预期方式工作的小解决方案:

代码要点:gist.github.com/1012130(带描述性注释)

点赞