YAML 파일을 다른 파일 안에 포함하려면 어떻게 해야 합니까?

StackOverflow https://stackoverflow.com/questions/528281

  •  22-08-2019
  •  | 
  •  

문제

따라서 "A"와 "B"라는 두 개의 YAML 파일이 있고 A의 내용을 B 안에 삽입하거나 배열처럼 기존 데이터 구조에 연결하거나 값과 같은 요소의 하위로 삽입하고 싶습니다. 특정 해시 키에 대해.

이것이 가능합니까?어떻게?그렇지 않은 경우 규범적 참조에 대한 포인터가 있습니까?

도움이 되었습니까?

해결책

아니요, Yaml에는 어떤 종류의 "가져 오기"또는 "포함"진술이 포함되어 있지 않습니다.

다른 팁

귀하의 질문은 파이썬 솔루션을 요구하지 않지만 여기에 사용하는 것이 있습니다. pyyaml.

pyyaml은 사용자 정의 생성자 (예 : !include) YAML 로더에. 이 솔루션이 상대 및 절대 파일 참조를 지원하도록 설정할 수있는 루트 디렉토리를 포함 시켰습니다.

수업 기반 솔루션

다음은 원래 응답의 글로벌 루트 변수를 피하는 클래스 기반 솔루션입니다.

이것 좀 봐 요점 메타 클래스를 사용하여 사용자 정의 생성자를 등록하는 유사하고 강력한 Python 3 솔루션의 경우.

import yaml
import os

class Loader(yaml.SafeLoader):

    def __init__(self, stream):

        self._root = os.path.split(stream.name)[0]

        super(Loader, self).__init__(stream)

    def include(self, node):

        filename = os.path.join(self._root, self.construct_scalar(node))

        with open(filename, 'r') as f:
            return yaml.load(f, Loader)

Loader.add_constructor('!include', Loader.include)

An example:

foo.yaml

a: 1
b:
    - 1.43
    - 543.55
c: !include bar.yaml

bar.yaml

- 3.6
- [1, 2, 3]

이제 파일을 사용하여로드 할 수 있습니다.

>>> with open('foo.yaml', 'r') as f:
>>>    data = yaml.load(f, Loader)
>>> data
{'a': 1, 'b': [1.43, 543.55], 'c': [3.6, [1, 2, 3]]}

사용하는 경우 Symfony의 Yaml 버전, 이것은 다음과 같이 가능합니다.

imports:
    - { resource: sub-directory/file.yml }
    - { resource: sub-directory/another-file.yml }

포함은 내가 아는 한 Yaml에서 직접 지원되지 않습니다. 그러나 메커니즘을 직접 제공해야하지만 일반적으로 쉽게 수행 할 수 있습니다.

나는 Python 앱에서 Yaml을 구성 언어로 사용 했으며이 경우 종종 다음과 같은 규칙을 정의합니다.

>>> main.yml <<<
includes: [ wibble.yml, wobble.yml]

그런 다음 내 (Python) 코드에서 다음을 수행합니다.

import yaml
cfg = yaml.load(open("main.yml"))
for inc in cfg.get("includes", []):
   cfg.update(yaml.load(open(inc)))

유일한 단점은 포함의 변수가 항상 기본 변수를 무시하고 "포함 된 다음 : 명령문이 main.yml 파일에 나타나는 위치를 변경하여 그 우선 순위를 변경할 수있는 방법이 없다는 것입니다.

약간 다른 시점에서 YAML은 파일 기반 마크 UP만큼 독점적으로 설계되지 않았기 때문에 포함하지 않습니다. AJAX 요청에 대한 응답으로 얻은 경우에는 무엇을 포함합니까?

@Josh_Bode의 답변을 확장하면 여기에 자체 포함 하위 클래스라는 장점이 있는 자체 PyYAML 솔루션이 있습니다. yaml.Loader.모듈 수준 전역 변수나 전역 상태 수정에 의존하지 않습니다. yaml 기준 치수.

import yaml, os

class IncludeLoader(yaml.Loader):                                                 
    """                                                                           
    yaml.Loader subclass handles "!include path/to/foo.yml" directives in config  
    files.  When constructed with a file object, the root path for includes       
    defaults to the directory containing the file, otherwise to the current       
    working directory. In either case, the root path can be overridden by the     
    `root` keyword argument.                                                      

    When an included file F contain its own !include directive, the path is       
    relative to F's location.                                                     

    Example:                                                                      
        YAML file /home/frodo/one-ring.yml:                                       
            ---                                                                   
            Name: The One Ring                                                    
            Specials:                                                             
                - resize-to-wearer                                                
            Effects: 
                - !include path/to/invisibility.yml                            

        YAML file /home/frodo/path/to/invisibility.yml:                           
            ---                                                                   
            Name: invisibility                                                    
            Message: Suddenly you disappear!                                      

        Loading:                                                                  
            data = IncludeLoader(open('/home/frodo/one-ring.yml', 'r')).get_data()

        Result:                                                                   
            {'Effects': [{'Message': 'Suddenly you disappear!', 'Name':            
                'invisibility'}], 'Name': 'The One Ring', 'Specials':              
                ['resize-to-wearer']}                                             
    """                                                                           
    def __init__(self, *args, **kwargs):                                          
        super(IncludeLoader, self).__init__(*args, **kwargs)                      
        self.add_constructor('!include', self._include)                           
        if 'root' in kwargs:                                                      
            self.root = kwargs['root']                                            
        elif isinstance(self.stream, file):                                       
            self.root = os.path.dirname(self.stream.name)                         
        else:                                                                     
            self.root = os.path.curdir                                            

    def _include(self, loader, node):                                    
        oldRoot = self.root                                              
        filename = os.path.join(self.root, loader.construct_scalar(node))
        self.root = os.path.dirname(filename)                           
        data = yaml.load(open(filename, 'r'))                            
        self.root = oldRoot                                              
        return data                                                      

파이썬 사용자의 경우 시도해 볼 수 있습니다 pyyaml-include.

설치

pip install pyyaml-include

용법

import yaml
from yamlinclude import YamlIncludeConstructor

YamlIncludeConstructor.add_to_loader_class(loader_class=yaml.FullLoader, base_dir='/your/conf/dir')

with open('0.yaml') as f:
    data = yaml.load(f, Loader=yaml.FullLoader)

print(data)

우리가 그런 것을 고려하십시오 파일 :

├── 0.yaml
└── include.d
    ├── 1.yaml
    └── 2.yaml
  • 1.yaml 의 내용 :
name: "1"
  • 2.yaml 의 내용 :
name: "2"

이름으로 파일을 포함합니다

  • 최상위 :

    만약에 0.yaml 였다:

!include include.d/1.yaml

우리는 얻을 것입니다 :

{"name": "1"}
  • 맵핑 :

    만약에 0.yaml 였다:

file1: !include include.d/1.yaml
file2: !include include.d/2.yaml

우리는 얻을 것입니다 :

  file1:
    name: "1"
  file2:
    name: "2"
  • 순서대로 :

    만약에 0.yaml 였다:

files:
  - !include include.d/1.yaml
  - !include include.d/2.yaml

우리는 얻을 것입니다 :

files:
  - name: "1"
  - name: "2"

메모:

파일 이름은 절대적 일 수 있습니다 ( /usr/conf/1.5/Make.yml) 또는 상대 (좋아요 ../../cfg/img.yml).

와일드 카드로 파일을 포함시킵니다

파일 이름에는 쉘 스타일 와일드 카드가 포함될 수 있습니다. 와일드 카드가 발견 한 파일에서로드 된 데이터는 순서로 설정됩니다.

만약에 0.yaml 였다:

files: !include include.d/*.yaml

우리는 얻을 것입니다 :

files:
  - name: "1"
  - name: "2"

메모:

  • 을 위한 Python>=3.5, 만약에 recursive 논쟁 !include 태그입니다 true, 패턴 “**” 모든 파일과 0 이상의 디렉토리 및 하위 디렉토리와 일치합니다.
  • 사용 “**” 큰 디렉토리 트리의 패턴은 재귀 검색으로 인해 많은 시간을 소비 할 수 있습니다.

활성화하기 위해 recursive 논쟁, 우리는 쓸 것이다 !include 태그를 입력하십시오 Mapping 또는 Sequence 방법:

  • 인수 Sequence 방법:
!include [tests/data/include.d/**/*.yaml, true]
  • 인수 Mapping 방법:
!include {pathname: tests/data/include.d/**/*.yaml, recursive: true}

불행히도 Yaml 은이 표준을 제공하지 않습니다.

그러나 Ruby를 사용하는 경우 Ruby Yaml 라이브러리를 확장하여 요구하는 기능을 제공하는 보석이 있습니다.https://github.com/entwanderer/yaml_extend

@maxy-b에서 사용한 솔루션이 멋지게 보입니다. 그러나 그것은 중첩 된 포함로 나에게 성공하지 못했습니다. 예를 들어 config_1.yaml에 config_2.yaml이 포함되면 config_3.yaml이 포함 된 경우 로더에 문제가있었습니다. 그러나 단순히 새 로더 클래스를로드시 자체로 지적하면 작동합니다! 구체적으로, 오래된 _include 함수를 매우 약간 수정 된 버전으로 바꾸면

def _include(self, loader, node):                                    
     oldRoot = self.root                                              
     filename = os.path.join(self.root, loader.construct_scalar(node))
     self.root = os.path.dirname(filename)                           
     data = yaml.load(open(filename, 'r'), loader = IncludeLoader)                            
     self.root = oldRoot                                              
     return data

반사시 나는 다른 의견에 동의합니다. 입력 스트림이 파일이 아닐 수도 있지만 매우 유용합니다!

어쩌면 이것은 당신에게 영감을 줄 수 있습니다.

https://docs.openstack.org/infra/jenkins-job-builder/definition.html#inclusion-tags

- job: name: test-job-include-raw-1 builders: - shell: !include-raw: include-raw001-hello-world.sh

표준 YAML 1.2에는 기본적 으로이 기능이 포함되지 않습니다. 그럼에도 불구하고 많은 구현이 그렇게 할 확장을 제공합니다.

나는 자바와 함께 그것을 달성하는 방법을 제시하고 snakeyaml:1.24 (Java 라이브러리를 구문 분석/방출 할 수있는 Java 라이브러리) 다음 목표를 달성하기 위해 사용자 정의 YAML 태그를 생성 할 수 있습니다 (여러 YAML 파일에 정의 된 테스트 스위트를로드하는 데 사용하는 것을 볼 수 있습니다. 대상을 포함합니다 test: 마디):

# ... yaml prev stuff

tests: !include
  - '1.hello-test-suite.yaml'
  - '3.foo-test-suite.yaml'
  - '2.bar-test-suite.yaml'

# ... more yaml document

다음은 처리를 허용하는 1 급 Java입니다 !include 꼬리표. 파일은 ClassPath (Maven Resources Directory)에서로드됩니다.

/**
 * Custom YAML loader. It adds support to the custom !include tag which allows splitting a YAML file across several
 * files for a better organization of YAML tests.
 */
@Slf4j   // <-- This is a Lombok annotation to auto-generate logger
public class MyYamlLoader {

    private static final Constructor CUSTOM_CONSTRUCTOR = new MyYamlConstructor();

    private MyYamlLoader() {
    }

    /**
     * Parse the only YAML document in a stream and produce the Java Map. It provides support for the custom !include
     * YAML tag to split YAML contents across several files.
     */
    public static Map<String, Object> load(InputStream inputStream) {
        return new Yaml(CUSTOM_CONSTRUCTOR)
                .load(inputStream);
    }


    /**
     * Custom SnakeYAML constructor that registers custom tags.
     */
    private static class MyYamlConstructor extends Constructor {

        private static final String TAG_INCLUDE = "!include";

        MyYamlConstructor() {
            // Register custom tags
            yamlConstructors.put(new Tag(TAG_INCLUDE), new IncludeConstruct());
        }

        /**
         * The actual include tag construct.
         */
        private static class IncludeConstruct implements Construct {

            @Override
            public Object construct(Node node) {
                List<Node> inclusions = castToSequenceNode(node);
                return parseInclusions(inclusions);
            }

            @Override
            public void construct2ndStep(Node node, Object object) {
                // do nothing
            }

            private List<Node> castToSequenceNode(Node node) {
                try {
                    return ((SequenceNode) node).getValue();

                } catch (ClassCastException e) {
                    throw new IllegalArgumentException(String.format("The !import value must be a sequence node, but " +
                            "'%s' found.", node));
                }
            }

            private Object parseInclusions(List<Node> inclusions) {

                List<InputStream> inputStreams = inputStreams(inclusions);

                try (final SequenceInputStream sequencedInputStream =
                             new SequenceInputStream(Collections.enumeration(inputStreams))) {

                    return new Yaml(CUSTOM_CONSTRUCTOR)
                            .load(sequencedInputStream);

                } catch (IOException e) {
                    log.error("Error closing the stream.", e);
                    return null;
                }
            }

            private List<InputStream> inputStreams(List<Node> scalarNodes) {
                return scalarNodes.stream()
                        .map(this::inputStream)
                        .collect(toList());
            }

            private InputStream inputStream(Node scalarNode) {
                String filePath = castToScalarNode(scalarNode).getValue();
                final InputStream is = getClass().getClassLoader().getResourceAsStream(filePath);
                Assert.notNull(is, String.format("Resource file %s not found.", filePath));
                return is;
            }

            private ScalarNode castToScalarNode(Node scalarNode) {
                try {
                    return ((ScalarNode) scalarNode);

                } catch (ClassCastException e) {
                    throw new IllegalArgumentException(String.format("The value must be a scalar node, but '%s' found" +
                            ".", scalarNode));
                }
            }
        }

    }

}

와 함께 Symfony, YAML을 처리하면 YAML 파일을 간접적으로 할 수 있습니다. 속임수는 parameters 옵션. 예 :

common.yml

parameters:
    yaml_to_repeat:
        option: "value"
        foo:
            - "bar"
            - "baz"

config.yml

imports:
    - { resource: common.yml }
whatever:
    thing: "%yaml_to_repeat%"
    other_thing: "%yaml_to_repeat%"

결과는 다음과 동일합니다.

whatever:
    thing:
        option: "value"
        foo:
            - "bar"
            - "baz"
    other_thing:
        option: "value"
        foo:
            - "bar"
            - "baz"

아마도 질문이 질문을 받았을 때 뒷받침되지 않았지만 다른 Yaml 파일을 하나로 가져올 수 있습니다.

imports: [/your_location_to_yaml_file/Util.area.yaml]

온라인 참조는 없지만 이것은 나에게 효과적입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top