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


113

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

some_stuff: &some_stuff
 - a
 - b
 - c

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

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

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

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


6
구문 분석하는 언어가 아닌 YAML로이 작업을 수행하려는 이유는 무엇입니까?
Patrick Collins

7
매우 큰 YAML 파일에 중복을 건조
lfender6445

4
이것은 매우 나쁜 습관입니다. yaml을 따로 읽고, 배열을 Ruby에 모은 다음 yaml에 다시 써야합니다.
sawa

74
어떻게 마른 나쁜 습관이 되려고 노력하고 있습니까?
krak3n

13
@PatrickCollins 내에서 중복을 줄이기 위해 노력하고이 문제를 발견 .gitlab-ci.yml의 파일 불행하게도 나는 파서 여부를 제어 할 수 없습니다 GitLab CI 사용하는 :( 그
rink.attendant.6

답변:


41

일련의 셸 명령을 실행하는 것이 목표 인 경우 다음과 같이 수행 할 수 있습니다.

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

combined_stuff:
  - *some_stuff
  - d
  - e
  - f

이것은 다음과 동일합니다.

some_stuff: "a\nb\nc"

combined_stuff:
  - "a\nb\nc"
  - d
  - e
  - f

나는 이것을 내 gitlab-ci.yml(질문에 @ rink.attendant.6 코멘트에 대답하기 위해) 사용하고 있습니다.


requirements.txtgitlab에서 개인 저장소 를 지원하기 위해 사용하는 작업 예제 :

.pip_git: &pip_git
- git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com".insteadOf "ssh://git@gitlab.com"
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts

test:
    image: python:3.7.3
    stage: test
    script:
        - *pip_git
        - pip install -q -r requirements_test.txt
        - python -m unittest discover tests

use the same `*pip_git` on e.g. build image...

여기서 requirements_test.txt예를 들어 있습니다

-e git+ssh://git@gitlab.com/example/example.git@v0.2.2#egg=example


3
영리한. 지금은 Bitbucket 파이프 라인에서 사용하고 있습니다. 감사합니다
Dariop 19

* 여기에서는 후행 대시가 필요하지 않으며 끝에있는 파이프 만 있으면됩니다. * 이는 매우 긴 여러 줄 문에서 작업이 실패 할 때 어떤 명령이 실패했는지 명확하지 않기 때문에 열등한 솔루션입니다.
미나 누가

1
@MinaLuke, 무엇에 비해 열등합니까? 현재 답변 중 어느 것도 yaml 만 사용하여 두 항목을 병합하는 방법을 제공하지 않습니다 ... 또한 OP가 CI / CD에서 이것을 사용하기를 원한다는 질문에 아무것도 없습니다. 마지막으로 이것이 CI / CD에서 사용되는 경우 로깅은 yaml 선언이 아닌 사용 된 특정 CI / CD에만 의존합니다. 그래서, 당신이 언급하고있는 CI / CD는 나쁜 일을하는 CI / CD입니다. 이 답변의 yaml은 유효하며 OP의 문제를 해결합니다.
Jorge Leitao

@JorgeLeitao 규칙을 결합하는 데 사용하는 것 같습니다. 작동하는 gitlabci 예제를 제공 할 수 있습니까? 솔루션을 기반으로 무언가를 시도했지만 항상 유효성 검사 오류가 발생합니다.
niels

@niels, 작동하는 gitlabci 예제와 함께 예제를 추가했습니다. 일부 IDE는이 yaml이 유효하지 않더라도 유효하지 않은 것으로 표시합니다.
Jorge Leitao

26

업데이트 : 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방법의 예


21

이것은 작동하지 않습니다.

  1. 병합은 시퀀스가 ​​아닌 매핑에 대한 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 사전 인 병합 사전이 생성됩니다. 이 병합은 미리 수행해야하며 데이터를 복제 (공간 비효율적)하지만 값 조회가 빠릅니다. 라운드 트립 로더를 사용할 때 병합되지 않은 병합을 덤프 할 수 있기를 원하므로 별도로 유지해야합니다. 왕복 로딩의 결과로로드 된 데이터 구조와 같은 딕셔너리는 공간 효율적이지만 액세스 속도가 느립니다. 매번 수행해야 함). 물론 이러한 고려 사항은 비교적 작은 구성 파일의 경우 그다지 중요하지 않습니다.


다음 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

즉, 고정 된 시퀀스에 태그를 지정하면 본질적으로 다른 데이터 구조가됩니다.

명시 적 태그를 사용하는 것이 YAML 병합 키처럼 마술을하는 것보다 IMO가 더 좋습니다 <<. <<병합 키처럼 작동하고 싶지 않은 키가있는 매핑이 포함 된 YAML 파일이있는 경우, 예를 들어 설명에 C 연산자를 매핑 할 때 다른 작업을 수행 해야하는 경우 영어 (또는 다른 자연어).


9

하나의 항목 만 목록에 병합해야하는 경우 다음을 수행 할 수 있습니다.

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

-4

다음 조건에서 매핑을 병합 한 다음 해당 키를 목록으로 변환 할 수 있습니다.

  • jinja2 템플릿을 사용하는 경우
  • 품목 주문이 중요하지 않은 경우
some_stuff: &some_stuff
 a:
 b:
 c:

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

{{ combined_stuff | list }}

이 답변에 어떤 문제가 있습니까? 나는 그들이 논쟁을한다면 반대표를해도 상관 없습니다. 나는 그것을 사용할 수있는 사람들을 위해 대답을 지킬 것입니다.
sm4rk0

3
이 답변은 질문이 yml에서 수행하도록 요청할 때 jinja2 템플릿에 의존하기 때문일 수 있습니다. jinja2는 Python 환경을 필요로하며 OP가 DRY를 시도하면 비생산적입니다. 또한 많은 CI / CD 도구는 템플릿 단계를 허용하지 않습니다.
Jorge Leitao

@JorgeLeitao 감사합니다. 말이 되네요. Ansible 플레이 북과 템플릿을 개발하는 동안 YAML과 Jinja2를 함께 배웠고 다른 하나 없이는 생각할 수 없습니다
sm4rk0 19
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.