19

内部でエイリアスが使用されている 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 を作成することは、実際には少し暗い競争の場のように見えます。

4

3 に答える 3

15

<<エイリアス化されたマッピングを現在のマッピングにマージする必要があることを示すために を使用することは、コア Yaml 仕様の一部ではありませんが、タグ リポジトリの一部です

Ruby が提供する現在の Yaml ライブラリ – Psych – は、Ruby オブジェクトの簡単なシリアル化と逆シリアル化を可能にし、ハッシュのマージを含むタグ リポジトリでさまざまな暗黙的な型変換を使用できるメソッドdumpとメソッドを提供します。また、必要に応じて、より低レベルの Yaml 処理を行うためのツールも提供します。残念ながら、タグ リポジトリの特定の部分を選択的に無効または有効にすることは容易ではありません。これは、全か無かの問題です。特に、の処理は hash の処理にかなり組み込まれていますload<<<<

目的を達成する 1 つの方法は、Psych の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. これは、あなたの例に基づいた簡単な例であり、アンカーが1つしかないと仮定しています:

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

(更新: 別の質問で、複数のアンカーでこれを行う方法が尋ねられました。シリアル化するときにアンカー名を保持するためのおそらくより良い方法を思いつきました。)

于 2012-10-18T21:39:46.090 に答える
3

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

ただし、<<データ内のは、エイリアス化されたハッシュを、エイリアスではなくなった新しいハッシュにマージします。

于 2012-10-18T16:36:23.577 に答える
1

Psychを試しましたか?ここで精神に関する別の質問。

于 2012-08-02T10:16:07.403 に答える