Is there YAML syntax for sharing part of a list or map?

So, I know I can do something like this:

sitelist: &sites
- www.foo.com
- www.bar.com


anotherlist: *sites

And have sitelist and anotherlist both contain www.foo.com and www.bar.com. However, what I really want is for anotherlist to also contain www.baz.com, without having to repeat www.foo.com and www.baz.com.

Doing this gives me a syntax error in the YAML parser:

sitelist: &sites
- www.foo.com
- www.bar.com


anotherlist: *sites
- www.baz.com

Just using anchors and aliases it doesn't seem possible to do what I want without adding another level of substructure, such as:

sitelist: &sites
- www.foo.com
- www.bar.com


anotherlist:
- *sites
- www.baz.com

Which means the consumer of this YAML file has to be aware of it.

Is there a pure YAML way of doing something like this? Or will I have to use some post-YAML processing, such as implementing variable substitution or auto-lifting of certain kinds of substructure? I'm already doing that kind of post-processing to handle a couple of other use-cases, so I'm not totally averse to it. But my YAML files are going to be written by humans, not machine generated, so I would like to minimise the number of rules that need to be memorised by my users on top of standard YAML syntax.

I'd also like to be able to do the analogous thing with maps:

namedsites: &sites
Foo: www.foo.com
Bar: www.bar.com


moresites: *sites
Baz: www.baz.com

I've had a search through the YAML spec, and couldn't find anything, so I suspect the answer is just "no you can't do this". But if anyone has any ideas that would be great.


EDIT: Since there have been no answers, I'm presuming that no one has spotted anything I haven't in the YAML spec and that this can't be done at the YAML layer. So I'm opening up the question to idea for post-processing the YAML to help with this, in case anyone finds this question in future.

78903 次浏览

(回答我自己的问题,以防我正在使用的解决方案对任何将来搜索它的人都有用)

由于没有纯 YAML 的方法来实现这一点,我将把它实现为 YAML 解析器和实际使用配置文件的代码之间的“语法转换”。因此,我的核心应用程序根本不必担心任何人性化的冗余避免措施,只需直接对结果结构进行操作即可。

The structure I'm going to use looks like this:

foo:
MERGE:
- - a
- b
- c
- - 1
- 2
- 3

它将被转换为相当于:

foo:
- a
- b
- c
- 1
- 2
- 3

或者,用地图:

foo:
MERGE:
- fork: a
spoon: b
knife: c
- cup: 1
mug: 2
glass: 3

将改为:

foo:
fork: a
spoon: b
knife: c
cup: 1
mug: 2
glass: 3

更正式地说,在调用 YAML 解析器从配置文件中获取本机对象之后,但是在将对象传递给应用程序的其余部分之前,我的应用程序将在对象图中查找包含单个键 MERGE的映射。与 MERGE关联的值必须是列表列表或映射列表; 任何其他子结构都是错误的。

在列表列表的情况下,包含 MERGE的整个映射将被按出现顺序连接在一起的子列表所替换。

在地图列表的例子中,包含 MERGE的整个地图将被一个包含子地图中所有键/值对的地图所替换。在键重叠的地方,将使用 MERGE列表中最后出现的子映射的值。

上面给出的示例没有那么有用,因为您可以直接编写所需的结构。它更有可能表现为:

foo:
MERGE:
- *salt
- *pepper

允许您创建一个列表或映射,其中包含在其他地方使用的节点 saltpepper中的所有内容。

(我一直给出 foo:的外部映射,以表明 MERGE必须是其映射中的 只有键,这意味着除非没有其他顶级名称,否则 MERGE不能以顶级名称的形式出现)

The merge key type is probably what you want. It uses a special << mapping key to indicate merges, allowing an alias to a mapping (or a sequence of such aliases) to be used as an initializer to merge into a single mapping. Additionally, you can still explicitly override values, or add more that weren't present in the merge list.

It's important to note that it works with mappings, not sequences as your first example. This makes sense when you think about it, and your example looks like it probably doesn't need to be sequential anyway. Simply changing your sequence values to mapping keys should do the trick, as in the following (untested) example:

sitelist: &sites
? www.foo.com  # "www.foo.com" is the key, the value is null
? www.bar.com


anotherlist:
<< : *sites    # merge *sites into this mapping
? www.baz.com  # add extra stuff

有些事情需要注意。首先,由于 <<是一个键,因此每个节点只能指定一次。其次,当使用序列作为值时,顺序是有意义的。这在本例中并不重要,因为没有相关的值,但是值得注意。

为了从这里的两个答案中澄清一些问题,对于列表,YAML 不直接支持这一点(但是对于字典,它是受支持的,请参阅 kittemon 的答案)。

要借鉴 Kittemon 的答案,请注意您可以使用替代语法创建具有 null 值的映射

foo:
<< : myanchor
bar:
baz:

而不是建议的语法

foo:
<< : myanchor
? bar
? baz

Like Kittemon's suggestion, this will allow you to use references to anchors within the mapping and avoid the sequence issue. I found myself needing to do this after discovering that the Symfony Yaml component v2.4.4 doesn't recorgnize the ? bar syntax.

正如前面的答案所指出的,在 YAML 中没有对扩展列表的内置支持。我提供了另一种方式来实现它自己。想想这个:

defaults: &defaults
sites:
- www.foo.com
- www.bar.com
     

setup1:
<<: *defaults
sites+:
- www.baz.com

This will be processed into:

defaults:
sites:
- www.foo.com
- www.bar.com


setup1:
sites:
- www.foo.com
- www.bar.com
- www.baz.com

其思想是将以“ +”结尾的键的内容合并到不包含“ +”的相应键。我在 Python 中实现了这一点,并发布了 给你