YAML에서 배열을 병합하고 루비를 통해로드하고 싶습니다.
some_stuff: &some_stuff
- a
- b
- c
combined_stuff:
<<: *some_stuff
- d
- e
- f
결합 된 배열을 다음과 같이 갖고 싶습니다. [a,b,c,d,e,f]
오류가 발생합니다. 블록 매핑을 구문 분석하는 동안 예상 키를 찾지 못했습니다.
YAML에서 배열을 어떻게 병합합니까?
YAML에서 배열을 병합하고 루비를 통해로드하고 싶습니다.
some_stuff: &some_stuff
- a
- b
- c
combined_stuff:
<<: *some_stuff
- d
- e
- f
결합 된 배열을 다음과 같이 갖고 싶습니다. [a,b,c,d,e,f]
오류가 발생합니다. 블록 매핑을 구문 분석하는 동안 예상 키를 찾지 못했습니다.
YAML에서 배열을 어떻게 병합합니까?
답변:
일련의 셸 명령을 실행하는 것이 목표 인 경우 다음과 같이 수행 할 수 있습니다.
# 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.txt
gitlab에서 개인 저장소 를 지원하기 위해 사용하는 작업 예제 :
.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
이 게시물은 다음 컨텍스트를 가정합니다.
lfender6445는 YAML 파일 내에서 두 개 이상의 목록을 병합하고, 병합 된 목록을 구문 분석 할 때 하나의 단일 목록으로 표시하도록합니다.
이는 원하는 목록이 매핑의 자식 요소로 나타나는 매핑에 YAML 앵커를 할당하여 간단히 얻을 수 있습니다. 그러나 이에 대한주의 사항이 있습니다 (아래 "함정"참조).
아래 예에는 3 개의 매핑 ( list_one, list_two, list_three
)과 적절한 경우 이러한 매핑을 참조하는 3 개의 앵커 및 별칭이 있습니다.
YAML 파일이 프로그램에로드되면 원하는 목록을 얻지 만로드 후 약간의 수정이 필요할 수 있습니다 (아래의 함정 참조).
list_one : & id001 - ㅏ -b - 씨 list_two : & id002 -전자 -f -g list_three : & id003 -h -나 - 제이 list_combined : -* id001 -* id002 -* id003
## list_combined [ [ "ㅏ", "비", "씨" ], [ "이자형", "에프", "지" ], [ "h", "나는", "제이" ] ]
이 접근 방식을 사용하면 YAML의 별칭 및 앵커 기능을 사용하여 병합 된 목록을 만들 수 있습니다.
출력 결과는 중첩 된 목록 목록이지만 flatten
메서드를 사용하여 쉽게 변환 할 수 있습니다 .
flatten
방법의 예flatten
;; 배열 배열 병합 / 편 평화flatten
;; http://ruby-doc.org/core-2.2.2/Array.html#method-i-flattenflatten
;; https://softwareengineering.stackexchange.com/a/254676/23884이것은 작동하지 않습니다.
병합은 시퀀스가 아닌 매핑에 대한 YAML 사양에서만 지원됩니다.
병합 키 <<
다음에 키 / 값 구분 기호 :
와 참조 인 값을 사용 하여 완전히 혼합 한 다음 동일한 들여 쓰기 수준의 목록을 계속 사용합니다.
이것은 올바른 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 연산자를 매핑 할 때 다른 작업을 수행 해야하는
경우 영어 (또는 다른 자연어).
다음 조건에서 매핑을 병합 한 다음 해당 키를 목록으로 변환 할 수 있습니다.
some_stuff: &some_stuff
a:
b:
c:
combined_stuff:
<<: *some_stuff
d:
e:
f:
{{ combined_stuff | list }}