web-dev-qa-db-ja.com

YAML配列をマージする方法は?

YAMLで配列をマージし、Ruby-

some_stuff: &some_stuff
 - a
 - b
 - c

combined_stuff:
  <<: *some_stuff
  - d
  - e
  - f

結合された配列を[a,b,c,d,e,f]

エラーが表示されます:ブロックマッピングの解析中に予期されるキーが見つかりませんでした

YAMLで配列をマージするにはどうすればよいですか?

71
lfender6445

更新:2019-07-01 14:06:12

  • :この質問に対する別の回答は、 代替アプローチの更新 。で大幅に編集されました。
    • 更新された回答には、この回答の回避策の代替案が記載されています。これは、セクションセクションにも追加されています。

状況

この投稿では、次のコンテキストを想定しています。

  • python 2.7
  • python YAMLパーサー

問題

lfender6445は、YAMLファイル内の2つ以上のリストをマージし、それらのマージされたリストを解析時に1つの特異なリストとして表示することを望んでいます。

ソリューション(回避策)

これは、YAMLアンカーをマッピングに割り当てるだけで取得できます。この場合、目的のリストはマッピングの子要素として表示されます。ただし、これには注意事項があります(以下の「落とし穴」を参照)。

次の例では、3つのマッピング(list_one, list_two, list_three)およびこれらのマッピングを適切に参照する3つのアンカーとエイリアス。

YAMLファイルがプログラムにロードされると、必要なリストが取得されますが、ロード後に少し変更する必要がある場合があります(以下の落とし穴を参照)。

元のYAMLファイル

 list_one:&id001 
-a 
-b 
-c 
 
 list_two:&id002 
-e 
-f 
-g 
 
 list_three:&id003 
-h 
-i 
-j 
 
 list_combined:
-* id001 
-* id002 
-* id003 

YAML.safe_loadの後の結果

 ## list_combined 
 
 
 "a"、
 "b"、
 "c" 
、
 
 "e"、
 "f"、
 "g" 
、
 
 "h"、
 "i"、
 "j" 
 
 

落とし穴

  • このアプローチはリストのネストされたリストを生成しますが、これは正確に望ましい出力ではないかもしれませんが、これはflattenメソッドを使用して後処理できます
  • YAMLアンカーとエイリアスの通常の注意事項 一意性と宣言順序に適用

結論

このアプローチでは、YAMLのエイリアスとアンカー機能を使用して、マージされたリストを作成できます。

出力結果はリストのネストされたリストですが、これはflattenメソッドを使用して簡単に変換できます。

こちらもご覧ください

@Anthonによる代替アプローチの更新

flattenメソッドの例

18
dreftymac

これは機能しません:

  1. マージはマッピング用のYAML仕様でのみサポートされ、シーケンス用ではありません

  2. マージキー<<の後にキー/値区切り文字:と参照である値が続くことにより、物事が完全に混ざり合った後、次のリストに進みます。同じインデントレベル

これは正しいYAMLではありません:

combine_stuff:
  x: 1
  - a
  - b

したがって、構文例はYAML拡張提案としては意味がありません。

複数の配列をマージするようなことをしたい場合は、次のような構文を検討してください。

combined_stuff:
  - <<: *s1, *s2
  - <<: *s3
  - d
  - e
  - f

ここで、s1s2s3は、新しいシーケンスにマージしてからdeを持つシーケンスのアンカー(図示せず)です。 fが追加されます。しかし、YAMLはこれらの種類の構造の深さを最初に解決しているため、マージキーの処理中に利用できる実際のコンテキストはありません。処理済みの値(アンカーシーケンス)を添付できる配列/リストはありません。

@dreftymacによって提案されたアプローチを取ることができますが、これには、どのネストされたシーケンスをフラットにするかを知る必要があるという大きな欠点があります(つまり、ロードされたデータ構造のルートから親シーケンスへの「パス」を知ることによって)、または、ネストされた配列/リストを検索してロードされたデータ構造を再帰的に調べ、それらをすべて無差別にフラット化します。

IMOのより良い解決策は、タグを使用して、フラット化を行うデータ構造を読み込むことです。これにより、フラット化する必要があるものとそうでないものを明確に示し、このフラット化を読み込み中に行うか、アクセス中に行うかを完全に制御できます。どちらを選択するかは、実装の容易さと、時間とストレージスペースの効率の問題です。これは、 mergekey機能 を実装するために行う必要がある同じトレードオフであり、常に最適な単一のソリューションはありません。

例えば。 my ruamel.yamlライブラリは、セーフローダーを使用する際にロード中にブルートフォースmerge-dictsを使用します。これにより、通常のPython dicts。このマージは完了しなければなりません。 -front、データを複製します(スペース効率が悪い)が、値のルックアップは高速です。ラウンドトリップローダーを使用する場合、マージされていないマージをダンプできるようにするため、別々に保持する必要があります。ラウンドトリップロードの結果、スペース効率はよくなりますが、マージでdict自体に見つからないキーを検索する必要があるため、アクセスが遅くなります(これはキャッシュされないため、毎回行う必要がありますもちろん)比較的小さな構成ファイルでは、このような考慮事項はあまり重要ではありません。


以下は、pythonのリストにマージのようなスキームを実装します。タグflattenを持つオブジェクトを使用し、オンザフライでリストでありtoflattenであるアイテムに再帰します。これら2つのタグは、YAMLファイルを持つことができます:

l1: &x1 !toflatten
  - 1 
  - 2
l2: &x2
  - 3 
  - 4
m1: !flatten
  - *x1
  - *x2
  - [5, 6]
  - !toflatten [7, 8]

(フローとブロックスタイルのシーケンスの使用は完全に任意であり、読み込まれた結果に影響しません)。

キーm1の値であるアイテムを繰り返し処理する場合、これはtoflattenでタグ付けされたシーケンスに「再帰」しますが、他のリスト(エイリアスの有無)を単一のアイテムとして表示します。

Pythonそれを達成するためのコードの1つの可能な方法は:

import sys
from pathlib import Path
import ruamel.yaml

yaml = ruamel.yaml.YAML()


@yaml.register_class
class Flatten(list):
   yaml_tag = u'!flatten'
   def __init__(self, *args):
      self.items = args

   @classmethod
   def from_yaml(cls, constructor, node):
       x = cls(*constructor.construct_sequence(node, deep=True))
       return x

   def __iter__(self):
       for item in self.items:
           if isinstance(item, ToFlatten):
               for nested_item in item:
                   yield nested_item
           else:
               yield item


@yaml.register_class
class ToFlatten(list):
   yaml_tag = u'!toflatten'

   @classmethod
   def from_yaml(cls, constructor, node):
       x = cls(constructor.construct_sequence(node, deep=True))
       return x



data = yaml.load(Path('input.yaml'))
for item in data['m1']:
    print(item)

どの出力:

1
2
[3, 4]
[5, 6]
7
8

ご覧のとおり、フラット化が必要なシーケンスでは、タグ付きシーケンスのエイリアスを使用するか、タグ付きシーケンスを使用できます。 YAMLでは次のことができません。

- !flatten *x2

、つまり、アンカーシーケンスにタグを付けます。これにより、基本的に別のデータ構造になります。

explicitタグを使用すると、YAMLマージキー<<のように魔法をかけるよりもIMOの方が優れています。マージキーのように振る舞わないキー<<を持つマッピングを持つYAMLファイルがある場合、他に何もする必要はありません。 C演算子を英語(またはその他の自然言語)の記述にマッピングするとき。

11
Anthon

1つの項目のみをリストにマージする必要がある場合は、次を実行できます。

fruit:
  - &banana
    name: banana
    colour: yellow

food:
  - *banana
  - name: carrot
    colour: orange

もたらす

fruit:
  - name: banana
    colour: yellow

food:
  - name: banana
    colour: yellow
  - name: carrot
    colour: orange
2
Tamlyn

これは次のようにして達成できます。

# note: no dash before commands
some_stuff: &some_stuff |-
    a
    b
    c

combined_stuff:
  - *some_stuff
  - d
  - e
  - f

私はこれをgitlab-ci.yml(質問に対する@ rink.attendant.6コメントへの回答)。

1
Jorge Leitão

次の条件下で、マッピングをマージして、キーをリストに変換できます。

  • jinja2テンプレートを使用している場合、
  • アイテムの順序が重要でない場合
some_stuff: &some_stuff
 a:
 b:
 c:

combined_stuff:
  <<: *some_stuff
  d:
  e:
  f:

{{ combined_stuff | list }}
0
sm4rk0