Как я могу включить файл YAML в другой?

Итак, у меня есть два файла YAML, "A" и "B", и я хочу, чтобы содержимое A было вставлено внутри B, либо сращено в существующую структуру данных, как массив, либо как дочерний элемент элемента, как значение для определенного хэш-ключа.

возможно ли это вообще? Как? Если нет, какие-либо указания на нормативную ссылку?

10 ответов


нет, YAML не включает какой-либо оператор "import" или "include".


ваш вопрос не требует решения Python, но вот один, использующий PyYAML.

PyYAML позволяет прикреплять пользовательские конструкторы (например,!include) к погрузчику YAML. Я включил корневой каталог, который можно установить так, чтобы это решение поддерживало относительные и абсолютные ссылки на файлы.

Класса

вот решение на основе класса, которое позволяет избежать глобальной корневой переменной моего оригинала ответ.

посмотреть этот gist для аналогичного, более надежного решения Python 3, которое использует метакласс для регистрации пользовательского конструктора.

import yaml
import os.path

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)

пример:

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]]}

Если вы используете версия YAML Symfony, это возможно, вот так:

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

includes не поддерживаются непосредственно в yaml, насколько я знаю, вам придется предоставить механизм самостоятельно, однако это, как правило, легко сделать.

я использовал yaml в качестве языка конфигурации в моих приложениях python, и в этом случае часто определяю такое соглашение:

>>> 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)))

единственным недостатком является то, что переменные в includes всегда будут переопределять переменные в main, и нет никакого способа измените этот приоритет, изменив, где в основном отображается оператор " includes:.файл yml.

на немного другой точке, yaml не поддерживает включает в себя, как его на самом деле не разработан как исключительно как файл на основе разметки. Что означает include, если вы получили его в ответ на запрос 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                                                      

к сожалению YAML не в его стандарт.

но если вы используете Ruby, есть драгоценный камень, обеспечивающий функциональность, которую вы просите, расширяя библиотеку ruby YAML: https://github.com/entwanderer/yaml_extend


Я думаю, что решение, используемое @maxy-B, выглядит отлично. Однако мне это не удалось с вложенными включениями. Например, если config_1.yaml включает config_2.yaml, который включает config_3.ямл возникла проблема с погрузчиком. Однако, если вы просто укажете новый класс загрузчика на себя при загрузке, он работает! В частности, если мы заменим старую функцию _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

по размышлении я согласен с другими комментариями, которые вложены загрузка не подходит для yaml в целом, так как входной поток может не быть файлом, но это очень полезно!


С Symfony, его обработка yaml косвенно позволит вам вложить файлы yaml. Фокус в том, чтобы использовать . например:

общие.в формате YML

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

конфиг.в формате 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"

возможно, это может вдохновить вас, попробуйте выровнять соглашения jbb:

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 в один:

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

хотя у меня нет интернет-ссылки, но это работает для меня.