读写YAML文件而不会破坏锚点和别名 [英] Read and write YAML files without destroying anchors and aliases

查看:249
本文介绍了读写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?

谢谢

推荐答案

此处的问题是锚点Yaml中的别名和别名是序列化的详细信息,因此在解析之后就不会成为数据的一部分,因此在将数据写回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方法,以便哈希是一个锚点,然后将锚点名称记录在@st变量中,以便以后可以识别别名,我们将其作为实例变量添加到哈希表上.

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映射.如果要使用其他类型来保留其锚点名称,则需要查看 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天全站免登陆