所以我有两个YAML文件,"A"和"B",我想要将A的内容插入到B中,或者拼接到现有的数据结构中,像数组一样,或者作为元素的子元素,比如值对于某个哈希键.
这有可能吗?怎么样?如果没有,任何指向规范参考的指针?
不,YAML不包含任何类型的"导入"或"包含"声明.
您的问题不是要求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)
一个例子:
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中的变量,并且无法通过更改"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
对于Python用户,您可以尝试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)
考虑我们有这样的YAML文件:
??? 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
)。
文件名可以包含shell样式的通配符。从通配符找到的文件中加载的数据将按顺序设置。
如果0.yaml
是:
files: !include include.d/*.yaml
我们会得到:
files: - name: "1" - name: "2"
?注意事项:
对于
Python>=3.5
,如果YAML标签的recursive
参数为,则该模式将匹配任何文件以及零个或多个目录和子目录。!include
true
“**”
“**”
由于递归搜索,在大型目录树中使用该模式可能会花费大量时间。
为了启用recursive
参数,我们!include
将以Mapping
或Sequence
模式写入标记:
Sequence
模式下的参数:
!include [tests/data/include.d/**/*.yaml, true]
Mapping
模式下的参数:
!include {pathname: tests/data/include.d/**/*.yaml, recursive: true}