如何在 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)
来反序列化一个复数),但我认为我不应该对那个方法进行猴子补丁。我注定要使用低级或中级发射,还是我错过了什么?
背景
我将描述这个项目,为什么它需要类,为什么它需要
(反)序列化。
项目
Small Eigen Collider 旨在为 Ruby 运行创建随机任务。
最初的目的是看看 Ruby 的不同实现
(例如,Rubinius 和 JRuby)在给定时返回相同的结果
相同的随机任务,但我发现它也有利于
检测段错误 Rubinius 和 YARV 的方法。
每个任务由以下部分组成:
receiver.send(method_name, *parameters, &block)
哪里
receiver
是一个随机选择的对象,而 method_name
是个随机选择的方法的名称,以及
*parameters
是一个数组随机选择的对象。
&block
不是很随机 - 基本上是相当于
{|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和鲁比纽斯
维护者修复了错误)
为什么需要类
我希望能够在我的 Small Eigen Collider 中使用类对象。
通常,他们将是接收者,但他们也可以是以下对象之一
参数。
例如,我发现对 YARV 进行段错误的一种方法是执行
Thread.kill(nil)
在这种情况下,接收者是类对象 Thread,参数是
[零]。 (错误报告:http://redmine.ruby-lang.org/issues/show/4367)
为什么需要(反)序列化
Small Eigen Collider 需要序列化有几个原因。
一种是使用随机数生成器生成一系列
每次都是随机任务是不切实际的。 JRuby 有一个不同的内置
随机数生成器,因此即使给定相同的 PRNG 种子,它也会
给 YARV 分配不同的任务。相反,我所做的是创建一个列表
随机任务一次(第一次运行 ruby
bin/small_eigen_collider),让初始运行序列化列表
任务到tasks.yml,然后有后续的运行
在tasks.yml中读取的程序(使用不同的Ruby实现)
文件以获取任务列表。
我需要序列化的另一个原因是我希望能够编辑
任务列表。如果我有一长串任务导致
段错误,我想将列表减少到所需的最小值
导致段错误。例如,有以下错误
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 的技巧可能永远不会奏效,因为真正的类处理不是在 psych/yaml 中实现的。
你可以拿这个 repo tenderlove/psych ,这是独立的库。
( gem : psych - 加载它,使用:
gem 'psych'; require 'psych'
并检查 Psych::VERSION
)正如您在 line 249-251 中看到的那样不处理具有匿名类 Class 的对象。
我建议您通过扩展此类处理来为 Psych 库做出贡献,而不是对类 Class 进行猴子修补。
所以在我看来,最终的 yaml 结果应该是这样的:
"--- !ruby/class String"
经过一晚的思考,我可以说,这个功能真的很棒!
更新
找到了一个似乎以预期方式工作的小解决方案:
代码要点: gist.github.com/1012130 (带有描述性注释)
关于ruby - 如何在 Psych 中反序列化类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5774580/