在不破坏锚点和别名的情况下读写 YAML 文件 [英] Read and write YAML files without destroying anchors and aliases

查看:23
本文介绍了在不破坏锚点和别名的情况下读写 YAML 文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

之前有人问过这个问题:阅读和在不破坏锚点和别名的情况下编写 YAML 文件?

This question has been asked before: Read and write YAML files without destroying anchors and aliases?

我想知道如何使用许多锚点和别名来解决这个问题?

I was wondering how to solve that problem with many anchors and aliases?

谢谢

推荐答案

这里的问题是 anchorsYaml 中的和别名 是序列化细节,因此在解析后不是数据的一部分,因此在将数据写回 Yaml 时不知道原始锚名称.为了在往返时保留锚名称,您需要在解析时将它们存储在某处,以便稍后在序列化时可用.在 Ruby 中,任何对象都可以有与之关联的实例变量,因此实现这一点的一种简单方法是将锚名称存储在相关对象的实例变量中.

The problem here is that anchors and aliases in Yaml are a serialization detail, and so aren’t part of the data after it’s been parsed, so the original anchor name isn’t known when writing the data back out to Yaml. In order to keep the anchor names when round tripping you need to store them somewhere when parsing so that they are available later when serializing. In Ruby any object can have instance variables associated with it, so an easy way to achieve this would be to store the anchor name in an instance variable of the objet in question.

继续前面的问题中的示例,对于哈希,我们可以更改重新定义的 revive_hash

Continuing from the example in the earlier question, for hashes we can change our redifined revive_hash method so that if the hash is an anchor then as well as recording the anchor name in the @st variable so later alises can be recognised, we add the it as an instance variable on the hash.

class ToRubyNoMerge < Psych::Visitors::ToRuby
  def revive_hash hash, o
    if o.anchor
      @st[o.anchor] = hash
      hash.instance_variable_set "@_yaml_anchor_name", o.anchor
    end

    o.children.each_slice(2) { |k,v|
      key = accept(k)
      hash[key] = accept(v)
    }
    hash
  end
end

请注意,这只影响作为锚点的 yaml 映射.如果你想让其他类型保留它们的锚名称,你需要查看 psych/visitors/to_ruby.rb 并确保在所有情况下都添加了名称.大多数类型可以通过覆盖 register 但还有其他几个;搜索 @st.

Note that this only affects yaml mappings that are anchors. If you want to have other types to keep their anchor name you’ll need to look at psych/visitors/to_ruby.rb and make sure the name is added in all cases. Most types can be included by overriding register but there are a couple of others; search for @st.

现在散列具有与之关联的所需锚名称,您需要让 Psych 在序列化它时使用它而不是对象 id.这可以通过继承 YAMLTree.当 YAMLTree 处理一个对象时,它 首先检查该对象是否已经被看到,如果已经看到,则为其发出别名.对于任何新对象,它 记录它已经看到该对象,以防以后需要创建别名.object_id 用作其中的键,因此您需要覆盖这两个方法来检查实例变量,如果存在则使用它:

Now that the hash has the desired anchor name associated with it, you need to make Psych use it instead of the object id when serializing it. This can be done by subclassing YAMLTree. When YAMLTree processes an object, it first checks to see if that object has been seen already, and emits an alias for it if it has. For any new objects, it records that it has seen the object in case it needs to create an alias later. The object_id is used as the key in this, so you need to override those two methods to check for the instance variable, and use that instead if it exists:

class MyYAMLTree < Psych::Visitors::YAMLTree

  # check to see if this object has been seen before
  def accept target
    if anchor_name = target.instance_variable_get('@_yaml_anchor_name')
      if @st.key? anchor_name
        oid         = anchor_name
        node        = @st[oid]
        anchor      = oid.to_s
        node.anchor = anchor
        return @emitter.alias anchor
      end
    end

    # accept is a pretty big method, call super to avoid copying
    # it all here. super will handle the cases when it's an object
    # that's been seen but doesn't have '@_yaml_anchor_name' set
    super
  end

  # record object for future, using '@_yaml_anchor_name' rather
  # than object_id if it exists
  def register target, yaml_obj
    anchor_name = target.instance_variable_get('@_yaml_anchor_name') || target.object_id
    @st[anchor_name] = yaml_obj
    yaml_obj
  end
end

现在你可以像这样使用它(与上一个问题不同,在这种情况下你不需要创建自定义发射器):

Now you can use it like this (unlike the previous question, you don’t need to create a custom emitter in this case):

builder = MyYAMLTree.new
builder << data

tree = builder.tree

puts tree.yaml # returns a string

# alternativelty write direct to file:
File.open('a_file.yml', 'r+') do |f|
  tree.yaml f
end

这篇关于在不破坏锚点和别名的情况下读写 YAML 文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆