Подтвердить что ты не робот

Чтение и запись файлов YAML без уничтожения якорей и псевдонимов?

Мне нужно открыть файл YAML с использованием псевдонимов внутри него:

defaults: &defaults
  foo: bar
  zip: button

node:
  <<: *defaults
  foo: other

Это, очевидно, распространяется на эквивалентный документ YAML:

defaults:
  foo: bar
  zip: button

node:
  foo: other
  zip: button

Какой YAML::load читает его как.

Мне нужно установить новые ключи в этом документе YAML, а затем записать его обратно на диск, сохранив исходную структуру как можно больше.

Я просмотрел YAML:: Store, но это полностью разрушает псевдонимы и привязки.

Есть ли что-нибудь доступное, что могло бы что-то делать по строкам:

thing = Thing.load("config.yml")
thing[:node][:foo] = "yet another"

Сохранение документа назад:

defaults: &defaults
  foo: bar
  zip: button

node:
  <<: *defaults
  foo: yet another

?

Я решил использовать YAML для этого из-за того, что он хорошо справляется с этим псевдонимом, но запись YAML, которая содержит псевдонимы, в действительности выглядит немного мрачным игровым полем.

4b9b3361

Ответ 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

(Обновить: еще один вопрос спросил, как это сделать с более чем одним якорем, где я придумал возможно лучший способ сохранить имена привязок при сериализации.)

Ответ 2

YAML имеет псевдонимы, и они могут совершать кругосветное путешествие, но вы отключите его путем слияния хэшей. <<, поскольку ключ отображения кажется нестандартным расширением для YAML (как в 1.8 syck, так и в 1.9 psych).

require 'rubygems'
require 'yaml'

yaml = <<EOS
defaults: &defaults
  foo: bar
  zip: button

node: *defaults
EOS

data = YAML.load yaml
print data.to_yaml

печатает

--- 
defaults: &id001 
  zip: button
  foo: bar
node: *id001

но << в ваших данных сглаживает алиасный хеш в новый, который больше не является псевдонимом.

Ответ 3

Попробуйте Psych? Другой вопрос с психологией здесь.