web-dev-qa-db-ja.com

リストまたはマップの一部を共有するためのYAML構文はありますか?

だから、私は次のようなことができることを知っています:

_sitelist: &sites
  - www.foo.com
  - www.bar.com

anotherlist: *sites
_

sitelistanotherlistの両方に_www.foo.com_と_www.bar.com_が含まれていること。ただし、本当に必要なのは、anotherlistalsoに_www.baz.com_を含め、_www.foo.com_と_www.baz.com_を繰り返す必要がないことです。

これを行うと、YAMLパーサーで構文エラーが発生します。

_sitelist: &sites
  - www.foo.com
  - www.bar.com

anotherlist: *sites
  - www.baz.com
_

アンカーとエイリアスを使用するだけでは、次のような下位レベルのサブ構造を追加しないと、私がやりたいことを実行できないようです。

_sitelist: &sites
  - www.foo.com
  - www.bar.com

anotherlist:
  - *sites
  - www.baz.com
_

つまり、このYAMLファイルのコンシューマーはそれを認識している必要があります。

このようなことを行う純粋なYAMLの方法はありますか?または、変数置換の実装や特定の種類の下部構造の自動リフティングなど、YAML後の処理を使用する必要がありますか?私はすでに他のいくつかのユースケースを処理するためにそのような後処理を行っているので、私はそれを完全に嫌っていません。ただし、YAMLファイルは、マシンで生成されたものではなく、人間によって書き込まれるため、標準のYAML構文に加えて、ユーザーが記憶する必要のあるルールの数を最小限に抑えたいと思います。

また、マップを使って同様のことができるようになりたいです。

_namedsites: &sites
  Foo: www.foo.com
  Bar: www.bar.com

moresites: *sites
  Baz: www.baz.com
_

YAML spec を検索しましたが、何も見つかりませんでしたので、答えは「これはできません」と思われます。しかし、誰かが素晴らしいアイデアを持っているなら。


EDIT:答えがなかったので、YAML仕様にないものを誰も発見していないと推測しています。 YAMLレイヤーで行われます。したがって、将来この質問を見つけた場合に備えて、これを支援するためにYAMLを後処理するためのアイデアへの質問を公開しています。

77
Ben

マージキータイプ は、おそらくあなたが望むものです。特別な<<マッピングキーを使用してマージを示し、マッピングのエイリアス(またはそのようなエイリアスのシーケンス)を単一のマッピングにマージする初期化子として使用できるようにします。さらに、明示的に値をオーバーライドしたり、マージリストに存在しなかった値を追加したりできます。

最初の例としてのシーケンスではなく、マッピングで機能することに注意することが重要です。これについて考えると、これは理にかなっており、例はおそらくとにかくシーケンシャルである必要はないように見えます。シーケンス値をマッピングキーに変更するだけで、次の(テストされていない)例のようにトリックを実行できます。

sitelist: &sites
  ? www.foo.com  # "www.foo.com" is the key, the value is null
  ? www.bar.com

anotherlist:
  << : *sites    # merge *sites into this mapping
  ? www.baz.com  # add extra stuff

いくつかの注意点。まず、<<はキーであるため、ノードごとに1回しか指定できません。第二に、値としてシーケンスを使用する場合、順序は重要です。関連する値がないため、これはここの例では重要ではありませんが、注意する価値があります。

49
kittemon

前の回答が指摘したように、YAMLでリストを拡張するための組み込みのサポートはありません。自分で実装する別の方法を提供しています。このことを考慮:

defaults: &defaults
  sites:
    - www.foo.com
    - www.bar.com

setup1:
  <<: *defaults
  sites+:
    - www.baz.com

これは次のように処理されます。

defaults:
  sites:
    - www.foo.com
    - www.bar.com

setup1:
  sites:
    - www.foo.com
    - www.bar.com
    - www.baz.com

アイデアは、「+」で終わるキーの内容を、「+」のない対応するキーにマージすることです。これをPythonで実装し、 here で公開しました。

楽しい!

16

ここの2つの回答から何かを明確にするために、これはリストのYAMLで直接サポートされていません(ただし、辞書ではサポートされています。kittemonの回答を参照してください)。

6
asmeurer

Kittemonの答えを便乗するには、代替構文を使用してnull値を使用してマッピングを作成できることに注意してください

foo:
    << : myanchor
    bar:
    baz:

推奨される構文の代わりに

foo:
    << : myanchor
    ? bar
    ? baz

Kittemonの提案のように、これにより、マッピング内のアンカーへの参照を使用して、シーケンスの問題を回避できます。 Symfony Yamlコンポーネントv2.4.4が? bar構文を再認識しないことを発見した後、これを行う必要があることに気付きました。

4
beef_boolean

(私が使用しているソリューションが将来これを検索する人に役立つ場合に備えて、私自身の質問に答えます)

これを行う純粋なYAMLの方法がないため、YAMLパーサーと設定ファイルを実際に使用するコードの間にある「構文変換」としてこれを実装します。したがって、私のコアアプリケーションは、人間に優しい冗長性回避策について一切心配する必要はなく、結果の構造に直接作用するだけです。

使用する構造は次のようになります。

foo:
  MERGE:
    - - a
      - b
      - c
    - - 1
      - 2
      - 3

これは次のものに変換されます:

foo:
  - a
  - b
  - c
  - 1
  - 2
  - 3

または、マップで:

foo:
  MERGE:
    - fork: a
      spoon: b
      knife: c
    - cup: 1
      mug: 2
      glass: 3

に変換されます:

foo:
  fork: a
  spoon: b
  knife: c
  cup: 1
  mug: 2
  glass: 3

より正式には、YAMLパーサーを呼び出して構成ファイルからネイティブオブジェクトを取得した後、オブジェクトをアプリケーションの残りの部分に渡す前に、私のアプリケーションはオブジェクトグラフをたどって単一のキーMERGEを含むマッピングを探します。 MERGEに関連付けられた値は、リストのリストまたはマップのリストのいずれかでなければなりません。その他の下位構造はエラーです。

リストの場合、MERGEを含むマップ全体が、出現順に連結された子リストに置き換えられます。

マップの場合、MERGEを含むマップ全体が、子マップ内のすべてのキー/値のペアを含む単一のマップに置き換えられます。キーに重複がある場合、MERGEリストの最後にある子マップの値が使用されます。

上記の例は、必要な構造を直接記述できたため、それほど有用ではありません。次のように表示される可能性が高くなります。

foo:
  MERGE:
    - *salt
    - *pepper

他の場所で使用されているノードsaltおよびpepperのすべてを含むリストまたはマップを作成できます。

(_foo:は、MERGEがマッピング内のonlyキーでなければならないことを示すための外部マップです。つまり、MERGEは、他のトップレベル名)

4
Ben