如何在Psych中反序列化类?

时间:2011-04-25 02:04:05

标签: ruby yaml 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那种方法。

我注定要处理低水平或中等水平的发射,还是我遗失了什么?

背景

我将描述项目,为什么需要类,以及它为什么需要 (DE)的序列化。

项目

Small Eigen Collider旨在为Ruby创建随机任务。 最初的目的是看看Ruby的不同实现 (例如,Rubinius和JRuby)在给出时返回相同的结果 相同的随机任务,但我发现它也有好处 检测破坏Rubinius和YARV的方法。

每项任务由以下内容组成:

receiver.send(method_name, *parameters, &block)

其中receiver是随机选择的对象,method_name是。{ 随机选择的方法的名称,*parameters是一个数组 随机选择的对象。 &block不是很随意 - 基本上就是这样 相当于{|o| o.inspect}

例如,如果receiver是“a”,则method_name为:casecmp,和 参数是[“b”],然后你要调用

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

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

"a".casecmp("b")

Small Eigen Collider运行此代码,并记录这些输入和 还有返回值。在这个例子中,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

但如果你把两者放在一起,那就确实如此。但我开始时 成千上万的任务,需要把它缩回到那两个 任务。

反序列化返回现有的类对象是否有意义 在这种背景下,还是你认为有更好的方法?

2 个答案:

答案 0 :(得分:1)

我目前研究的现状:

要使您所需的行为正常工作,您可以使用我上面提到的解决方法。

这里有格式良好的代码示例:

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 - 要加载它,请使用:gem 'psych'; require 'psych'并检查Psych::VERSION

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

而不是monkeypatching class Class我建议你通过扩展这个类处理来为Psych lib做贡献。

所以在我看来,最终的yaml结果应该是:"--- !ruby/class String"

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


<强>更新

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

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

答案 1 :(得分:1)

Psych维护者已经实现了classesmodules的序列化和反序列化。它现在在Ruby中!