题
所以我有两个其文件的"A"和"B"和我想的内容待插入的内部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据我所知,你必须提供自己的机制然而,这通常是很容易的事情。
我已经使用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)))
唯一的缺点是,在包括变量将总是覆盖中的变量主,并且没有办法通过改变其中“包括更改优先级:语句出现在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
为蟒蛇用户,你可以试试 pyyaml-包括.
安装
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
's的内容:
name: "1"
2.yaml
's的内容:
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
).
包括文件通过符
文件名称可以包含壳式通配符。数据加载的文件(s)通过找到符,将设置在一个序列。
如果 0.yaml
为:
files: !include include.d/*.yaml
我们会得到:
files:
- name: "1"
- name: "2"
ℹ 注意到:
- 对于
Python>=3.5
, ,如果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}
不幸的是YAML不在其标准提供这一点。
但如果你使用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
在反射我同意的其他意见,即嵌套装载不适合于一般YAML作为输入流可能不是一个文件,但它是非常有用!
也许这能鼓舞你,尽量对准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 1.2本身不包括该功能。然而许多实现提供了一些扩展这样做。
我介绍了Java和snakeyaml:1.24
实现它的一种方式(Java库来解析/发射YAML文件),它允许创建一个自定义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
下面是一个级的Java,允许处理所述!include
标签。文件从类路径(Maven的资源目录)加载:
/**
* 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]
虽然我没有任何的在线参考,但这个对我的作品。