ruby yaml

ruby - ¿Lee y escriba archivos YAML sin destruir anclas y alias?



(3)

¿Has probado Psych ? Otra pregunta con la psicología here .

Necesito abrir un archivo YAML con alias usados ​​dentro de él:

defaults: &defaults foo: bar zip: button node: <<: *defaults foo: other

Esto obviamente se expande a un documento equivalente de YAML de:

defaults: foo: bar zip: button node: foo: other zip: button

YAML::load lee como.

Necesito establecer nuevas claves en este documento YAML y luego volver a escribirlas en el disco, conservando la estructura original tanto como sea posible.

He consultado YAML::Store , pero esto destruye completamente los alias y los anclajes.

¿Hay algo disponible que podría algo a lo largo de las líneas de:

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

Guardando el documento de nuevo como:

defaults: &defaults foo: bar zip: button node: <<: *defaults foo: yet another

?

Opté por usar YAML para esto debido al hecho de que maneja bien este aliasing, pero escribir YAML que contiene alias parece ser un campo de juego de aspecto sombrío en la realidad.


El uso de << para indicar que un mapeo con alias debe fusionarse con el mapeo actual no es parte de la especificación central de Yaml, sino que es parte del repositorio de etiquetas .

La biblioteca actual de Yaml proporcionada por Ruby - Psych - proporciona los métodos de dump y load que permiten una fácil serialización y deserialización de los objetos de Ruby y usan varias conversiones de tipos implícitas en el repositorio de etiquetas, incluida << para fusionar hashes. También proporciona herramientas para hacer más procesamiento de Yaml de bajo nivel si lo necesita. Desafortunadamente, no permite fácilmente deshabilitar o habilitar de manera selectiva partes específicas del repositorio de etiquetas, es un asunto de todo o nada. En particular, el manejo de << está bastante relacionado con el manejo de hashes .

Una forma de lograr lo que desea es proporcionar su propia subclase de la clase ToRuby de Psych y anular este método, de modo que solo trate las claves de mapeo de << como literales. Esto implica anular un método privado en Psych, por lo que debe tener un poco de cuidado:

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

Entonces lo usarías así:

tree = Psych.parse your_data data = ToRubyNoMerge.new.accept tree

Con el Yaml de su ejemplo, los data verían como

{"defaults"=>{"foo"=>"bar", "zip"=>"button"}, "node"=>{"<<"=>{"foo"=>"bar", "zip"=>"button"}, "foo"=>"other"}}

Tenga en cuenta la << como una clave literal. Además, el hash debajo de la clave de data["defaults"] es el mismo hash que el que está debajo de la clave de data["node"]["<<"] , es decir, tienen el mismo object_id . Ahora puede manipular los datos como desee, y cuando los escriba como Yaml, los anclajes y alias seguirán en su lugar, aunque los nombres de los anclajes habrán cambiado:

data[''node''][''foo''] = "yet another" puts Yaml.dump data

produce (Psych utiliza el object_id del hash para asegurar nombres de ancla únicos (la versión actual de Psych ahora usa números secuenciales en lugar de object_id ))

--- defaults: &2151922820 foo: bar zip: button node: <<: *2151922820 foo: yet another

Si desea tener control sobre los nombres de anclaje, puede proporcionar su propio Psych::Visitors::Emitter . Aquí hay un ejemplo simple basado en su ejemplo y asumiendo que solo hay un ancla:

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

Cuando se usa con el hash de data modificado desde arriba:

#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

la salida es:

--- defaults: &defaults foo: bar zip: button node: <<: *defaults foo: yet another

( Actualización: otra pregunta preguntaba cómo hacer esto con más de un ancla, donde se me ocurrió una forma posiblemente mejor de mantener los nombres de ancla al serializar ).


YAML tiene alias y pueden redondearse, pero lo deshabilitas mediante la fusión de hash. << como clave de mapeo parece una extensión no estándar de YAML (tanto en 1.8''s syck como en 1.9''s psych).

require ''rubygems'' require ''yaml'' yaml = <<EOS defaults: &defaults foo: bar zip: button node: *defaults EOS data = YAML.load yaml print data.to_yaml

huellas dactilares

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

pero el << en sus datos combina el hash de alias en uno nuevo que ya no es un alias.