development

YAML 배열을 병합하는 방법은 무엇입니까?

big-blog 2020. 10. 22. 08:19
반응형

YAML 배열을 병합하는 방법은 무엇입니까?


YAML에서 배열을 병합하고 루비를 통해로드하고 싶습니다.

some_stuff: &some_stuff
 - a
 - b
 - c

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

결합 된 배열을 다음과 같이 갖고 싶습니다. [a,b,c,d,e,f]

오류가 발생했습니다. 블록 매핑을 구문 분석하는 동안 예상 키를 찾지 못했습니다.

YAML에서 배열을 어떻게 병합합니까?


업데이트 : 2019-07-01 14:06:12

  • 참고 :이 질문에 대한 또 다른 답변 은 대체 접근 방식에 대한 업데이트 와 함께 실질적으로 편집되었습니다 .
    • 업데이트 된 답변은이 답변의 대안에 대한 대안을 언급합니다. 아래 참고 항목 섹션 에 추가되었습니다 .

문맥

이 게시물은 다음 컨텍스트를 가정합니다.

  • 파이썬 2.7
  • 파이썬 YAML 파서

문제

lfender6445는 YAML 파일 내에서 두 개 이상의 목록을 병합하고, 병합 된 목록을 구문 분석 할 때 하나의 단일 목록으로 표시하도록합니다.

솔루션 (해결 방법)

이는 원하는 목록이 매핑의 자식 요소로 나타나는 매핑에 YAML 앵커를 할당하여 간단히 얻을 수 있습니다. 그러나 이에 대한주의 사항이 있습니다 (아래 "함정"참조).

아래 예에는 3 개의 매핑 ( list_one, list_two, list_three)과 적절한 경우 이러한 매핑을 참조하는 3 개의 앵커 및 별칭이 있습니다.

YAML 파일이 프로그램에로드되면 원하는 목록을 얻지 만로드 후 약간의 수정이 필요할 수 있습니다 (아래의 함정 참조).

원본 YAML 파일

  list_one : & id001
   - ㅏ
   -b
   - 씨

  list_two : & id002
   -전자
   -f
   -g

  list_three : & id003
   -h
   -나
   - 제이

  list_combined :
      -* id001
      -* id002
      -* id003

YAML.safe_load 이후의 결과

## list_combined
  [
    [
      "ㅏ",
      "비",
      "씨"
    ],
    [
      "이자형",
      "에프",
      "지"
    ],
    [
      "h",
      "나는",
      "제이"
    ]
  ]

함정

  • 이 접근 방식은 목록의 중첩 된 목록을 생성합니다. 이는 정확히 원하는 출력이 아닐 수 있지만 flatten 메서드를 사용하여 후 처리 할 수 ​​있습니다.
  • YAML 앵커 및 별칭에 대한 일반적인주의 사항 고유성과 선언 순서 신청

결론

이 접근 방식을 사용하면 YAML의 별칭 및 앵커 기능을 사용하여 병합 된 목록을 만들 수 있습니다.

출력 결과는 중첩 된 목록 목록이지만 flatten메서드를 사용하여 쉽게 변환 할 수 있습니다 .

또한보십시오

@Anthon의 업데이트 된 대체 접근 방식

flatten방법의


이것은 작동하지 않을 것입니다.

  1. merge는 시퀀스가 ​​아닌 매핑에 대한 YAML 사양에서만 지원됩니다.

  2. 병합 키 <<와 키 / 값 구분 기호 :및 참조 값을 사용 하여 완전히 혼합 한 다음 동일한 들여 쓰기 수준에서 목록을 계속 진행합니다.

이것은 올바른 YAML이 아닙니다.

combine_stuff:
  x: 1
  - a
  - b

따라서 예제 구문은 YAML 확장 제안으로도 이해되지 않습니다.

여러 배열 병합과 같은 작업을 수행하려면 다음과 같은 구문을 고려할 수 있습니다.

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

여기서 s1, s2, s3새 시퀀스로 병합 할 다음을 가지고 시퀀스 (도시하지 않음)에 앵커이다 d, e그리고 f그 추가는. 그러나 YAML은 이러한 종류의 구조 깊이를 먼저 해결하므로 병합 키 처리 중에 사용할 수있는 실제 컨텍스트가 없습니다. 처리 된 값 (고정 된 시퀀스)을 첨부 할 수있는 배열 / 목록이 없습니다.

@dreftymac에서 제안한 방식을 사용할 수 있지만, 이것은 어떤 중첩 시퀀스를 평탄화할지 알아야한다는 큰 단점이 있습니다 (즉,로드 된 데이터 구조의 루트에서 부모 시퀀스까지의 "경로"를 아는 것). 또는 중첩 된 배열 / 목록을 검색하는로드 된 데이터 구조를 재귀 적으로 걷고 모든 것을 무차별 적으로 평면화합니다.

더 나은 솔루션 IMO는 태그를 사용하여 평면화를 수행하는 데이터 구조를로드하는 것입니다. 이를 통해 병합해야하는 항목과 그렇지 않은 항목을 명확하게 표시 할 수 있으며이 병합이로드 중에 수행되는지 또는 액세스 중에 수행되는지를 완벽하게 제어 할 수 있습니다. 어느 것을 선택할지는 구현의 용이성과 시간 및 저장 공간의 효율성 문제입니다. 이것은 병합 기능 을 구현하는 데 필요한 것과 동일한 절충안 이며 항상 최상의 단일 솔루션은 없습니다.

예를 들어 내 ruamel.yaml라이브러리는 세이프 로더를 사용할 때로드하는 동안 무차별 대입 병합 사전을 사용하므로 일반 Python 사전 인 병합 사전이 생성됩니다. 이 병합은 미리 수행해야하며 데이터를 복제 (공간 비효율적)하지만 값 조회가 빠릅니다. 라운드 트립 로더를 사용할 때 병합되지 않은 병합을 덤프 할 수 있기를 원하므로 별도로 유지해야합니다. 왕복 로딩의 결과로로드 된 데이터 구조와 같은 dict는 공간 효율적이지만 액세스 속도가 느립니다. 병합시 dict 자체에서 찾을 수없는 키를 검색해야하기 때문입니다 (캐시되지 않으므로 매번 수행해야 함). 물론 이러한 고려 사항은 비교적 작은 구성 파일의 경우 그다지 중요하지 않습니다.


다음 flatten은 목록과 태그가있는 항목으로 즉석에서 반복되는 태그가 있는 객체를 사용하여 파이썬의 목록에 대한 병합 유사 체계를 구현합니다 toflatten. 이 두 태그를 사용하여 YAML 파일을 가질 수 있습니다.

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

(흐름 대 블록 스타일 시퀀스의 사용은 완전히 임의적이며로드 된 결과에 영향을주지 않습니다.)

키의 값인 항목을 반복 할 때이 m1태그는로 태그가 지정된 시퀀스로 "반복" toflatten되지만 다른 목록 (별칭 여부에 관계없이)을 단일 항목으로 표시합니다.

이를 달성하기 위해 Python 코드로 가능한 한 가지 방법은 다음과 같습니다.

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

, i.e. tag an anchored sequence, as this would essentially make it into a different datastructure.

Using explicit tags is IMO better than having some magic going on as with YAML merge keys <<. If nothing else you now have to go through hoops if you happen to have a YAML file with a mapping that has a key << that you don't want to act like a merge key, e.g. when you make a mapping of C operators to their descriptions in English (or some other natural language).


You can achieve this as follows:

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

combined_stuff:
  - *some_stuff
  - d
  - e
  - f

I have been using this on my gitlab-ci.yml (to answer @rink.attendant.6 comment on the question).


If you only need to merge one item into a list you can do

fruit:
  - &banana
    name: banana
    colour: yellow

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

which yields

fruit:
  - name: banana
    colour: yellow

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

You can merge mappings then convert their keys into a list, under these conditions:

  • if you are using jinja2 templating and
  • if item order is not important
some_stuff: &some_stuff
 a:
 b:
 c:

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

{{ combined_stuff | list }}

참고URL : https://stackoverflow.com/questions/24090177/how-to-merge-yaml-arrays

반응형