4

我有一个类FooHash.

class Foo < Hash
  # whatever Foo-specific methods/etc
end

当我使用 YAML 将其转储到文件时,它会写有指示类的标签。

> f = Foo.new
> f[:bar] = "baz"
> puts YAML.dump(f)
--- !ruby/hash:Foo
:bar: baz

我希望它只是写成一个普通的旧哈希(不是!ruby/hash:Foo

> puts YAML.dump({bar:"baz"})
---
:bar: baz

...这样我的数据的消费者就不需要知道Foo. 是否有一种魔术方法可以添加到我的类中以将其自身转换为序列化,或者是否有一个魔术选项可以传递给 YAML.dump?

当然,将一个Foo对象转换为散列很容易,但它们可能会嵌套在我想要转储的实际散列中的任何级别,我宁愿不必进行搜索和替换。

4

2 回答 2

4

您可以使用(文档很少)encode_withrepresent_map方法来实现这一点。要自定义对象的 YAML 序列化,您需要为其提供一个encode_with接受coder对象的方法,其中一个方法是represent_map.

class Foo < Hash

  # other methods ...

  def encode_with coder
    coder.represent_map nil, self
  end

end

现在YAML.dump只会将您的对象输出为普通哈希。

然而

有一点问题,因为有一个错误会导致此失败,并且仅在最新的 Gem 版本的 Psych 中修复。在当前最新的 Ruby 版本(ruby 2.0.0p247)中没有修复它。它已在 Ruby 主干中修复,因此以后的补丁版本应该没问题。

为了使用它,您必须确保您使用的是最新的 Psych Gem,而不是与 Ruby 捆绑在一起的版本。这应该很容易

gem 'psych', '2.0.0'

在您需要 Yaml 之前,但似乎在 Ruby 2.0 中,由于某种我无法弄清楚的原因,这不起作用。不过,使用 Bundler 来指定 Gem 版本确实有效,因此Gemfile如果您还没有使用它,您可能需要在其中创建一个并指定 Psych。

于 2013-08-12T02:51:12.017 回答
0

搜索和替换其实还不错:

# Convert Hash/Array subclasses into plain hashes/arrays for YAML dump.
# Assumptions:
#   Hash keys will be simple objects - don't need to clear them
#   No custom objects containing Hash/Array subclass instances
def deep_clear_subclasses(obj, dedup = {})
  case obj
  when Hash
    return dedup[obj] if dedup.has_key? obj
    dedup[obj] = copy = {}
    obj.each {|k,v| copy[k] = deep_clear_subclasses(v, dedup)}
    copy
  when Array
    return dedup[obj] if dedup.has_key? obj
    obj.inject(dedup[obj] = []) {|a,v| a << deep_clear_subclasses(v,dedup)}
  else
    obj # not going to operate on other kinds of objects
  end
end
于 2013-08-12T17:37:30.380 回答