Max*_*Max 3 ruby parsing yaml psych emit
之前已经问过这个问题:在不破坏锚点和别名的情况下读取和写入YAML文件?
我想知道如何用许多锚和别名来解决这个问题?
谢谢
这里的问题是Yaml中的锚和别名是序列化细节,因此在解析之后不是数据的一部分,因此在将数据写回Yaml时不知道原始锚名称.为了在循环跳转时保留锚名称,您需要在解析时将它们存储在某处,以便稍后在序列化时可用.在Ruby中,任何对象都可以拥有与之关联的实例变量,因此实现此目的的一种简单方法是将锚名称存储在相关对象的实例变量中.
继续前面问题中的示例,对于哈希,我们可以更改我们的redifined revive_hash方法,这样如果哈希是一个锚,那么在@st变量中记录锚名称,以便稍后可以识别alises,我们将其添加为实例散列上的变量.
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
Run Code Online (Sandbox Code Playgroud)
请注意,这仅影响作为锚点的yaml映射.如果您想让其他类型保留其锚名称,您需要查看psych/visitors/to_ruby.rb并确保在所有情况下都添加该名称.大多数类型都可以通过覆盖来包含,register但还有其他几种类型; 寻找@st.
既然散列具有与之关联的所需锚名称,则在序列化时需要让Psych使用它而不是对象id.这可以通过子类化来完成YAMLTree.当YAMLTree处理一个对象,它首先检查以查看是否该对象已经被看到的,并且发射一个别名,如果它有.对于任何新对象,它记录它已经看到该对象以防以后需要创建别名.将object_id被用作这一目标的关键,所以你需要重写这两种方法来检查实例变量,而是它是否存在使用的是:
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
Run Code Online (Sandbox Code Playgroud)
现在您可以像这样使用它(与上一个问题不同,在这种情况下您不需要创建自定义发射器):
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
Run Code Online (Sandbox Code Playgroud)