동일한 YAML 파일의 다른 곳에서 YAML "설정"을 참조하는 방법은 무엇입니까?


145

다음 YAML이 있습니다.

paths:
  patha: /path/to/root/a
  pathb: /path/to/root/b
  pathc: /path/to/root/c

/path/to/root/세 경로에서 제거하여 어떻게 이것을 "정상화" 하고 다음과 같이 자체 설정으로 사용할 수 있습니까?

paths:
  root: /path/to/root/
  patha: *root* + a
  pathb: *root* + b
  pathc: *root* + c

분명히 그것은 유효하지 않습니다, 나는 방금 그것을 만들었습니다. 실제 구문은 무엇입니까? 할 수 있습니까?


답변:


127

나는 그것이 가능하지 않다고 생각합니다. "노드"를 재사용 할 수 있지만 그 일부는 사용할 수 없습니다.

bill-to: &id001
    given  : Chris
    family : Dumars
ship-to: *id001

이것은 완벽하게 유효한 YAML 및 필드 given이며 블록 family에서 재사용됩니다 ship-to. 스칼라 노드는 같은 방식으로 재사용 할 수 있지만 내부 내용을 변경하고 YAML 내부에서 경로의 마지막 부분을 추가 할 수있는 방법은 없습니다.

반복이 당신을 귀찮게한다면 응용 프로그램이 root속성을 인식 하고 절대적이지 않은 상대 경로로 추가하는 것이 좋습니다 .


1
알았어 고마워, 맞아 root. 더 큰.
Andrew Bullock

2
허용 된 답변이 정확하지 않습니다. 해결책에 대한 내 대답을 참조하십시오.
Chris Johnson

어떻게,이 작업을 수행하는 청구에 어디 우리가 가져온하는 다른 파일에 선박이-을 정의?
Prateek Jain

@PrateekJain : 여러 파일을 다루는 경우 여기에 나열된 것과 같은 독립형 YAML 향상 라이브러리를 평가하는 것이 가장 좋습니다. github.com/dreftymac/dynamic.yaml/blob/master/…
dreftymac

1
yaml.org/spec/1.2/spec.html의 예제 2.9를 참조하십시오 . 또한 멋진 스칼라를 참조 할 수도 있습니다
akostadinov

72

예, 맞춤 태그를 사용합니다. 파이썬에서 예제 !join, 배열에 태그 조인 문자열 만들기 :

import yaml

## define custom tag handler
def join(loader, node):
    seq = loader.construct_sequence(node)
    return ''.join([str(i) for i in seq])

## register the tag handler
yaml.add_constructor('!join', join)

## using your sample data
yaml.load("""
paths:
    root: &BASE /path/to/root/
    patha: !join [*BASE, a]
    pathb: !join [*BASE, b]
    pathc: !join [*BASE, c]
""")

결과 :

{
    'paths': {
        'patha': '/path/to/root/a',
        'pathb': '/path/to/root/b',
        'pathc': '/path/to/root/c',
        'root': '/path/to/root/'
     }
}

인수로 구성된 배열은 !join문자열로 변환 될 수있는 한 모든 데이터 유형의 요소를 여러 개 가질 수 있으므로 !join [*a, "/", *b, "/", *c]예상 한 것과 같습니다.


2
나는 당신의 솔루션을 좋아한다. 코딩이 간단하고 약간 덜 읽기 쉬운 YAML의 비용으로 채굴한다.
Anthon

7
이 답변에는 더 많은 투표권이 있습니다. YAML 사양에 따라 기술적으로 가장 정확한 답변입니다. 그러나 실제 YAML 구현에 따라 한 가지주의 사항이 있지만 실제로 전체 YAML 사양을 구현하는 사람은 거의 없습니다. 파이썬의 pyyaml은 사양과의 균일 성 측면에서 다른 많은 것보다 뛰어납니다.
dreftymac

5
문제는 yaml 파일에서 값을 참조하는 것 같습니다. 그 주위에 다른 코드 레이어를 추가하는 것은 내가 선호하는 솔루션이 아닙니다.
user2020056

1
@ChrisJohnson이 답변에 감사드립니다.이 구문을 나열한 참조 문서가 있는지 궁금합니다. YAML 사양이 웹의 여러 위치에서 설명되어 있으므로 동일한 참조를보고 싶습니다. 감사!
user5359531

3
이 솔루션은 나를 위해 작동하지 않았지만 ( python3?) 위의 간단한 수정으로 예상대로 작동합니다. 구체적으로 :yaml.SafeLoader.add_constructor(tag='!join', constructor=join) yaml.load(open(fpth, mode='r'), Loader=yaml.SafeLoader)
benjaminmgross

20

이것을 보는 또 다른 방법은 단순히 다른 필드를 사용하는 것입니다.

paths:
  root_path: &root
     val: /path/to/root/
  patha: &a
    root_path: *root
    rel_path: a
  pathb: &b
    root_path: *root
    rel_path: b
  pathc: &c
    root_path: *root
    rel_path: c

5

YML 정의 :

dir:
  default: /home/data/in/
  proj1: ${dir.default}p1
  proj2: ${dir.default}p2
  proj3: ${dir.default}p3 

백리향 어딘가에

<p th:utext='${@environment.getProperty("dir.default")}' />
<p th:utext='${@environment.getProperty("dir.proj1")}' /> 

출력 : / home / data / in / / home / data / in / p1


@ AndrewBullock 나는 이것이 당신의 문제를 정확하게 해결하기 때문에 받아 들일만한 대답이어야한다고 생각합니다.
Honza Zidek

5
아니요, YAML에서 변수의 기본 사용법이 아니며 사양 버전에 지정되어 있지 않습니다. 일부 테스트 후에는 작동하지 않습니다.
Arthur Lacoste

2
이것은 아마도 yaml을 미리 처리 한 것 (예 : maven-resources-plugin 필터링)을 사용하여 Pavol에
효과적

1
표준 Yaml이 아님
Dan Niero

3

이 기능을 수행하는 Packagist에서 사용할 수있는 라이브러리를 작성했습니다. https://packagist.org/packages/grasmash/yaml-expander

YAML 파일 예 :

type: book
book:
  title: Dune
  author: Frank Herbert
  copyright: ${book.author} 1965
  protaganist: ${characters.0.name}
  media:
    - hardcover
characters:
  - name: Paul Atreides
    occupation: Kwisatz Haderach
    aliases:
      - Usul
      - Muad'Dib
      - The Preacher
  - name: Duncan Idaho
    occupation: Swordmaster
summary: ${book.title} by ${book.author}
product-name: ${${type}.title}

논리 예 :

// Parse a yaml string directly, expanding internal property references.
$yaml_string = file_get_contents("dune.yml");
$expanded = \Grasmash\YamlExpander\Expander::parse($yaml_string);
print_r($expanded);

결과 배열 :

array (
  'type' => 'book',
  'book' => 
  array (
    'title' => 'Dune',
    'author' => 'Frank Herbert',
    'copyright' => 'Frank Herbert 1965',
    'protaganist' => 'Paul Atreides',
    'media' => 
    array (
      0 => 'hardcover',
    ),
  ),
  'characters' => 
  array (
    0 => 
    array (
      'name' => 'Paul Atreides',
      'occupation' => 'Kwisatz Haderach',
      'aliases' => 
      array (
        0 => 'Usul',
        1 => 'Muad\'Dib',
        2 => 'The Preacher',
      ),
    ),
    1 => 
    array (
      'name' => 'Duncan Idaho',
      'occupation' => 'Swordmaster',
    ),
  ),
  'summary' => 'Dune by Frank Herbert',
);

확장기 개념을 좋아합니다!
기 illa 로데릭

2

일부 언어에서는 대체 라이브러리를 사용할 수 있습니다. 예를 들어 tampax 는 YAML 처리 변수의 구현입니다.

const tampax = require('tampax');

const yamlString = `
dude:
  name: Arthur
weapon:
  favorite: Excalibur
  useless: knife
sentence: "{{dude.name}} use {{weapon.favorite}}. The goal is {{goal}}."`;

const r = tampax.yamlParseString(yamlString, { goal: 'to kill Mordred' });
console.log(r.sentence);

// output : "Arthur use Excalibur. The goal is to kill Mordred."

1

귀하의 예제가 유효하지 것은 단지 당신이 당신의 스칼라를 시작하는 예약 된 문자를 선택했기 때문에. 를 *예약되지 않은 다른 문자로 바꾸면 (일부 사양의 일부로 거의 사용되지 않으므로 ASCII가 아닌 문자를 사용하는 경향이 있음) 완전히 합법적 인 YAML로 끝납니다.

paths:
  root: /path/to/root/
  patha: ♦root♦ + a
  pathb: ♦root♦ + b
  pathc: ♦root♦ + c

파서가 사용하는 언어로 매핑을위한 표준 표현으로로드되며 마술처럼 아무것도 확장하지 않습니다.
그렇게하려면 다음 Python 프로그램에서와 같이 로컬 기본 객체 유형을 사용하십시오.

# coding: utf-8

from __future__ import print_function

import ruamel.yaml as yaml

class Paths:
    def __init__(self):
        self.d = {}

    def __repr__(self):
        return repr(self.d).replace('ordereddict', 'Paths')

    @staticmethod
    def __yaml_in__(loader, data):
        result = Paths()
        loader.construct_mapping(data, result.d)
        return result

    @staticmethod
    def __yaml_out__(dumper, self):
        return dumper.represent_mapping('!Paths', self.d)

    def __getitem__(self, key):
        res = self.d[key]
        return self.expand(res)

    def expand(self, res):
        try:
            before, rest = res.split(u'♦', 1)
            kw, rest = rest.split(u'♦ +', 1)
            rest = rest.lstrip() # strip any spaces after "+"
            # the lookup will throw the correct keyerror if kw is not found
            # recursive call expand() on the tail if there are multiple
            # parts to replace
            return before + self.d[kw] + self.expand(rest)
        except ValueError:
            return res

yaml_str = """\
paths: !Paths
  root: /path/to/root/
  patha: ♦root♦ + a
  pathb: ♦root♦ + b
  pathc: ♦root♦ + c
"""

loader = yaml.RoundTripLoader
loader.add_constructor('!Paths', Paths.__yaml_in__)

paths = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)['paths']

for k in ['root', 'pathc']:
    print(u'{} -> {}'.format(k, paths[k]))

인쇄됩니다 :

root -> /path/to/root/
pathc -> /path/to/root/c

확장은 즉시 수행되며 중첩 된 정의를 처리하지만 무한 재귀를 호출하지 않도록주의해야합니다.

덤퍼를 지정하면 즉시 확장으로 인해로드 된 데이터에서 원래 YAML을 덤프 할 수 있습니다.

dumper = yaml.RoundTripDumper
dumper.add_representer(Paths, Paths.__yaml_out__)
print(yaml.dump(paths, Dumper=dumper, allow_unicode=True))

매핑 키 순서가 변경됩니다. 그것이 문제라면 (에서 가져온 ) 을 만들어야 self.d합니다.CommentedMapruamel.yaml.comments.py


0

파이썬에서 자체 라이브러리를 작성하여 다음과 같은 계층 구조로 디렉토리에서로드되는 변수를 확장했습니다.

/root
 |
 +- /proj1
     |
     +- config.yaml
     |
     +- /proj2
         |
         +- config.yaml
         |
         ... and so on ...

여기서 중요한 차이점은 모든 config.yaml파일이로드 된 후에 만 확장을 적용해야한다는 것 입니다. 여기서 다음 파일의 변수는 이전 파일의 변수를 재정의 할 수 있으므로 의사 코드는 다음과 같아야합니다.

env = YamlEnv()
env.load('/root/proj1/config.yaml')
env.load('/root/proj1/proj2/config.yaml')
...
env.expand()

추가 옵션으로 xonsh스크립트는 결과 변수를 환경 변수로 내보낼 수 있습니다 ( yaml_update_global_vars함수 참조 ).

스크립트 :

https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools/cmdoplib.yaml.py https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts /Tools/cmdoplib.yaml.xsh

찬성 :

  • 단순, 재귀 및 중첩 변수를 지원하지 않습니다
  • 정의되지 않은 변수를 자리 표시 자로 대체 할 수 있습니다 (${MYUNDEFINEDVAR} ->*$/{MYUNDEFINEDVAR} )로
  • 환경 변수에서 참조를 확장 할 수 있습니다 (${env:MYVAR} )
  • 경로 변수에서 모두 \\를 대체 할 수 있습니다 /( ${env:MYVAR:path})

단점 :

  • 중첩 변수를 지원하지 않으므로 중첩 사전에서 값을 확장 할 수 없습니다 (예 : ${MYSCOPE.MYVAR} : 구현되지 않음)
  • 자리 표시 자 삽입 후 재귀를 포함하여 확장 재귀를 감지하지 못함

0

Yglu를 사용하면 다음과 같이 예제를 작성할 수 있습니다.

paths:
  root: /path/to/root/
  patha: !? .paths.root + a
  pathb: !? .paths.root + b
  pathc: !? .paths.root + c

면책 조항 : 나는 Yglu의 저자입니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.