kch*_*kch 255 yaml transclusion
所以我有两个YAML文件,"A"和"B",我想要将A的内容插入到B中,或者拼接到现有的数据结构中,像数组一样,或者作为元素的子元素,比如值对于某个哈希键.
这有可能吗?怎么样?如果没有,任何指向规范参考的指针?
jam*_*her 290
不,YAML不包含任何类型的"导入"或"包含"声明.
Jos*_*ode 102
您的问题不是要求Python解决方案,而是使用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)
Run Code Online (Sandbox Code Playgroud)
一个例子:
foo.yaml
a: 1
b:
- 1.43
- 543.55
c: !include bar.yaml
Run Code Online (Sandbox Code Playgroud)
bar.yaml
- 3.6
- [1, 2, 3]
Run Code Online (Sandbox Code Playgroud)
现在可以使用以下方式加载文件:
>>> 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]]}
Run Code Online (Sandbox Code Playgroud)
dav*_*all 32
如果你正在使用Symfony的YAML版本,这是可能的,如下所示:
imports:
- { resource: sub-directory/file.yml }
- { resource: sub-directory/another-file.yml }
Run Code Online (Sandbox Code Playgroud)
小智 11
据我所知,YAML不直接支持包含,但您必须自己提供一种机制,这通常很容易实现.
我在我的python应用程序中使用YAML作为配置语言,在这种情况下通常定义这样的约定:
>>> main.yml <<<
includes: [ wibble.yml, wobble.yml]
Run Code Online (Sandbox Code Playgroud)
然后在我的(python)代码中我做:
import yaml
cfg = yaml.load(open("main.yml"))
for inc in cfg.get("includes", []):
cfg.update(yaml.load(open(inc)))
Run Code Online (Sandbox Code Playgroud)
唯一的缺点是包含中的变量将始终覆盖main中的变量,并且无法通过更改"includes:"语句出现在main.yml文件中的位置来更改该优先级.
在一个稍微不同的点上,YAML不支持包含,因为它的设计并不像基于文件的标记那样完全.如果你在回答一个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
Run Code Online (Sandbox Code Playgroud)
使用Yglu,您可以像这样导入其他文件:
亚米尔
foo: !? $import('B.yaml')
Run Code Online (Sandbox Code Playgroud)
B.yaml
bar: Hello
Run Code Online (Sandbox Code Playgroud)
$ yglu A.yaml
foo:
bar: Hello
Run Code Online (Sandbox Code Playgroud)
与$import函数一样,您也可以传递表达式作为参数:
dep: !- b
foo: !? $import($_.dep.toUpper() + '.yaml')
Run Code Online (Sandbox Code Playgroud)
这将给出与上面相同的输出。
免责声明:我是 Yglu 的作者。
对于Python用户,您可以尝试pyyaml-include。
pip install pyyaml-include
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
考虑我们有这样的YAML文件:
??? 0.yaml
??? include.d
??? 1.yaml
??? 2.yaml
Run Code Online (Sandbox Code Playgroud)
1.yaml 的内容:name: "1"
Run Code Online (Sandbox Code Playgroud)
2.yaml 的内容:name: "2"
Run Code Online (Sandbox Code Playgroud)
在顶层:
如果0.yaml是:
!include include.d/1.yaml
Run Code Online (Sandbox Code Playgroud)
我们会得到:
{"name": "1"}
Run Code Online (Sandbox Code Playgroud)
在映射中:
如果0.yaml是:
file1: !include include.d/1.yaml
file2: !include include.d/2.yaml
Run Code Online (Sandbox Code Playgroud)
我们会得到:
file1:
name: "1"
file2:
name: "2"
Run Code Online (Sandbox Code Playgroud)
按顺序:
如果0.yaml是:
files:
- !include include.d/1.yaml
- !include include.d/2.yaml
Run Code Online (Sandbox Code Playgroud)
我们会得到:
files:
- name: "1"
- name: "2"
Run Code Online (Sandbox Code Playgroud)
?注意事项:
文件名可以是绝对名称(如
/usr/conf/1.5/Make.yml)或相对名称(如../../cfg/img.yml)。
文件名可以包含shell样式的通配符。从通配符找到的文件中加载的数据将按顺序设置。
如果0.yaml是:
files: !include include.d/*.yaml
Run Code Online (Sandbox Code Playgroud)
我们会得到:
files:
- name: "1"
- name: "2"
Run Code Online (Sandbox Code Playgroud)
?注意事项:
- 对于
Python>=3.5,如果YAML标签的recursive参数为,则该模式将匹配任何文件以及零个或多个目录和子目录。!includetrue“**”“**”由于递归搜索,在大型目录树中使用该模式可能会花费大量时间。
为了启用recursive参数,我们!include将以Mapping或Sequence模式写入标记:
Sequence模式下的参数:!include [tests/data/include.d/**/*.yaml, true]
Run Code Online (Sandbox Code Playgroud)
Mapping模式下的参数:!include {pathname: tests/data/include.d/**/*.yaml, recursive: true}
Run Code Online (Sandbox Code Playgroud)
YML 标准没有指定执行此操作的方法。而这个问题并不局限于 YML。JSON 也有同样的限制。
许多使用基于 YML 或 JSON 的配置的应用程序最终都会遇到这个问题。当这种情况发生时,他们会制定自己的约定。
例如对于 swagger API 定义:
$ref: 'file.yml'
Run Code Online (Sandbox Code Playgroud)
例如对于 docker compose 配置:
services:
app:
extends:
file: docker-compose.base.yml
Run Code Online (Sandbox Code Playgroud)
或者,如果您想将 yml 文件的内容拆分为多个文件,例如内容树,您可以定义自己的文件夹结构约定并使用(现有)合并脚本。
| 归档时间: |
|
| 查看次数: |
148713 次 |
| 最近记录: |