Как я могу включить файл YAML в другой?
Итак, у меня есть два файла YAML, "A" и "B", и я хочу, чтобы содержимое A было вставлено внутри B, либо сращено в существующую структуру данных, как массив, либо как дочерний элемент элемента, как значение для определенного хэш-ключа.
возможно ли это вообще? Как? Если нет, какие-либо указания на нормативную ссылку?
10 ответов
ваш вопрос не требует решения 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]
хотя у меня нет интернет-ссылки, но это работает для меня.