ruby - 如何在 Psych 中反序列化类?

标签 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) 来反序列化一个复数),但我认为我不应该对那个方法进行猴子补丁。

我注定要使用低级或中级发射,还是我错过了什么?

背景

我将描述这个项目,为什么它需要类,为什么它需要
(反)序列化。

项目

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/

相关文章:

ruby-on-rails - 迪尔德 : Symbol not found: _rb_ary_new_from_values When trying to run foreman start

ruby-on-rails - Rails 生成错误 : No such file or directory - getcwd

Ruby:类 `_run_suite' 的未定义方法 `Test::Unit::Runner'

ruby - ruby 脚本中的 `#!` (shebang)(hashbang) 有问题

parsing - 覆盖 YAML 节点

r - 防止打印 LaTeX 注释

ruby - 在 mac osX (Lion) 上为 ruby​​ 安装 libyaml

ruby - 如何使用带有 HTTP 基本身份验证的 Passenger(在 Apache 上)部署多个 Rack/Sinatra 应用程序?

python - Kubernetes Python API 客户端 : execute full yaml file

c++ - 使用 Python 在 OpenCV 中解析 XML 或 YML