读写YAML文件而不会破坏锚点和别名 [英] Read and write YAML files without destroying anchors and aliases
问题描述
之前曾有人问过这个问题:阅读并在不破坏锚点和别名的情况下编写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屋!