0

我有这个旧代码Sych正在使用:

yaml_as "tag:yaml.org,2002:#{self}"
def to_yaml(opts = {})
  YAML::quick_emit(self, opts) do |out|
    out.map(taguri, to_yaml_style) do |map|
      map.add('name', name)
      map.add('address', full_address.upcase) if full_address?
    end
  end
end

输出类似的东西:

--- !Contact
name: SMOKE OIL
address: |-
  SMOKE OIL
  1 RUE DE LA PAIX
  75002 PARIS
  FRANCE

现在,我正在升级旧代码,然后Psych,我阅读了文档并做了:

yaml_as "tag:yaml.org,2002:#{self}"
def encode_with(coder)
  coder['name'] = name
  coder['address'] = full_address.upcase if full_address?
end

那确实:

--- !Contact
name: SMOKE OIL
address: ! "SMOKE OIL\n1 RUE DE LA PAIX\n75002 PARIS\nFRANCE"

这是不错的 YAML,但它应该是 whois 服务器的输出,而且人类可读性较差……</p>

于是,我又回到了 doc,看了一下第二种做事方式,也就是构建 AST。现在,除非我什么都没看到,否则没有什么可以解释如何获取您构建的 AST,并将其插入 Psych.dump(obj) 仍然可以工作的方式......</p>

我试着做(没有太大希望):

a = Psych::Nodes::Scalar(full_address.upcase)
a.style = Psych::Nodes::LITTERAL
coder['address'] = a if full_address?

但是,很明显,它并没有达到我希望的效果……我也尝试过:

def encode_with(coder)
  Psych::Nodes::Mapping.new.tap do |map|
    map.children << Psych::Nodes::Scalar.new("name")
    map.children << Psych::Nodes::Scalar.new(name)
    map.children << Psych::Nodes::Scalar.new("address")
    a = Psych::Nodes::Scalar.new(full_address.upcase)
    a.style = 4
    map.children << a
  end
end

但是,我看不到如何将其插入编码器......

此外,在执行递归操作时,答案需要起作用,这是一个Contact对象,但可以要求一个Domain包含一些联系人的对象,我希望它尽可能 DRY :-)

那么,有人对如何做到这一点有提示吗?

4

1 回答 1

1

如果你想创建自己的 AST,那么你不能使用Psych.dump. Psych.dump使用 Psych 默认值创建自己的 AST。在您的情况下,您想要自定义 AST 创建过程。

查看源代码Psych.dump可以看到它使用 aPsych::Visitors::YAMLTree创建 AST。您可以对其进行子类化并自定义它如何处理您的Contact类以获得您想要的输出。特别是您需要覆盖accept方法

这是一个简单的示例,只是Contact该类的特殊情况:

class MyYAMLTree < Psych::Visitors::YAMLTree
  def accept target
    return super unless target.is_a? Contact

    @emitter.start_mapping(nil, "tag:yaml.org,2002:#{target.class}", false, Psych::Nodes::Mapping::BLOCK)

    @emitter.scalar 'name', nil, nil, true, false, Psych::Nodes::Scalar::ANY
    @emitter.scalar target.name, nil, nil, true, false, Psych::Nodes::Scalar::ANY

    @emitter.scalar 'address', nil, nil, true, false, Psych::Nodes::Scalar::ANY

    #this is the where we make the address string a literal
    @emitter.scalar target.full_address, nil, nil, true, false, Psych::Nodes::Scalar::LITERAL

    @emitter.end_mapping
  end
end

请注意,它Psych::Visitors::YAMLTree是调用的类encode_with,这将为您的类完全绕过它。

为了使用它,请使用类似的东西(这基本上是Psych.dump使用 MyYAMLTree 的简化版本):

def my_yaml o
  visitor = MyYAMLTree.new
  visitor << o
  visitor.tree.yaml
end

这显然只是一个简单的例子,但希望它能为您指明正确的方向。

于 2012-09-30T20:33:07.533 回答