我如何让Ruby YAML将哈希子类转储为简单的哈希?

时间:2013-08-12 00:30:59

标签: ruby hash yaml

我有一个Foo的子类Hash

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对象转换为哈希值,但是它们可能会嵌套在我要转储的实际哈希中的任何级别,而我宁愿不必这样做搜索和替换。

2 个答案:

答案 0 :(得分:3)

您可以使用(记录不完整的)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只会将您的对象输出为普通哈希值。

然而

有一个问题,因为有一个错误会导致失败并且是only fixed in the latest Gem version of Psych。在当前最新的Ruby版本(ruby 2.0.0p247)中修复了 not 。它is fixed in Ruby trunk所以后来的补丁发布应该没问题。

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

一样简单
gem 'psych', '2.0.0'

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

答案 1 :(得分: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