Ответ 1
Использование символа <<
для обозначения сглаженного отображения должно быть объединено с текущим отображением, не является частью основной спецификации Ямля, но это part репозитория тегов.
Текущая библиотека Yaml, предоставляемая Ruby-Psych, предоставляет методы dump
и load
, которые позволяют легко сериализовать и десериализовать объекты Ruby и использовать различные неявные преобразования типов в репозитории тегов, включая <<
для объединения хэшей, Он также предоставляет инструменты для обработки более низкого уровня Yaml, если вам это нужно. К сожалению, это не позволяет легко выборочно отключить или включить определенные части репозитория тегов - это все или ничего. В частности, обработка обработки <<
довольно испечена при обработке хэшей.
Один из способов добиться того, что вы хотите, - предоставить свой собственный подкласс класса Psychs ToRuby
и переопределить этот метод, так что он просто рассматривает клавиши сопоставления <<
как литералы. Это включает в себя переопределение частного метода в Psych, поэтому вам нужно быть осторожным:
require 'psych'
class ToRubyNoMerge < Psych::Visitors::ToRuby
def revive_hash hash, o
@st[o.anchor] = hash if o.anchor
o.children.each_slice(2) { |k,v|
key = accept(k)
hash[key] = accept(v)
}
hash
end
end
Затем вы будете использовать его следующим образом:
tree = Psych.parse your_data
data = ToRubyNoMerge.new.accept tree
С помощью Yaml из вашего примера data
будет выглядеть примерно так:
{"defaults"=>{"foo"=>"bar", "zip"=>"button"},
"node"=>{"<<"=>{"foo"=>"bar", "zip"=>"button"}, "foo"=>"other"}}
Обратите внимание на <<
как литерал. Также хэш под клавишей data["defaults"]
является тем же самым хэшем, что и под клавишей data["node"]["<<"]
, т.е. Имеет тот же object_id
. Теперь вы можете манипулировать данными по своему усмотрению, и когда вы напишете его как Yaml, якоря и псевдонимы все равно будут на месте, хотя имена анкеров будут изменены:
data['node']['foo'] = "yet another"
puts Yaml.dump data
(Psych использует object_id
хэша для обеспечения уникальных имен привязок (текущая версия Psych теперь использует последовательные номера, а не object_id
)):
---
defaults: &2151922820
foo: bar
zip: button
node:
<<: *2151922820
foo: yet another
Если вы хотите управлять именами привязок, вы можете предоставить свой Psych::Visitors::Emitter
. Вот простой пример, основанный на вашем примере и предполагающий, что только один якорь:
class MyEmitter < Psych::Visitors::Emitter
def visit_Psych_Nodes_Mapping o
o.anchor = 'defaults' if o.anchor
super
end
def visit_Psych_Nodes_Alias o
o.anchor = 'defaults' if o.anchor
super
end
end
При использовании с измененным хешем data
сверху:
#create an AST based on the Ruby data structure
builder = Psych::Visitors::YAMLTree.new
builder << data
ast = builder.tree
# write out the tree using the custom emitter
MyEmitter.new($stdout).accept ast
вывод:
---
defaults: &defaults
foo: bar
zip: button
node:
<<: *defaults
foo: yet another
(Обновить: еще один вопрос спросил, как это сделать с более чем одним якорем, где я придумал возможно лучший способ сохранить имена привязок при сериализации.)