This is not going to work:
merge is only supported by the YAML specifications for mappings and not for sequences
you are completely mixing things by having a merge key <<
followed by the key/value separator :
and a value that is a
reference and then continue with a list at the same indentation
level
This is not correct YAML:
combine_stuff:
x: 1
- a
- b
So your example syntax would not even make sense as a YAML extension proposal.
If you want to do something like merging multiple arrays you might want to consider a syntax like:
combined_stuff:
- <<: *s1, *s2
- <<: *s3
- d
- e
- f
where s1
, s2
, s3
are anchors on sequences (not shown) that you
want to merge into a new sequence and then have the d
, e
and f
appended to that. But YAML is resolving these kind of structures depth
first, so there is no real context available during the processing
of the merge key. There is no array/list available to you where you
could attach the processed value (the anchored sequence) to.
You can take the approach as proposed by @dreftymac, but this has the huge disadvantage that you
somehow need to know which nested sequences to flatten (i.e. by knowing the "path" from the root
of the loaded data structure to the parent sequence), or that you recursively walk the loaded
data structure searching for nested arrays/lists and indiscriminately flatten all of them.
A better solution IMO would be to use tags to load data structures
that do the flattening for you. This allows for clearly denoting what
needs to be flattened and what not and gives you full control over
whether this flattening is done during loading, or done during
access. Which one to choose is a matter of ease of implementation and
efficiency in time and storage space. This is the same trade-off that needs to be made
for implementing the merge key feature and
there is no single solution that is always the best.
E.g. my ruamel.yaml
library uses the brute force merge-dicts during
loading when using its safe-loader, which results in merged
dictionaries that are normal Python dicts. This merging has to be done
up-front, and duplicates data (space inefficient) but is fast in value
lookup. When using the round-trip-loader, you want to be able to dump
the merges unmerged, so they need to be kept separate. The dict like
datastructure loaded as a result of round-trip-loading, is space
efficient but slower in access, as it needs to try and lookup a key
not found in the dict itself in the merges (and this is not cached, so
it needs to be done every time). Of course such considerations are
not very important for relatively small configuration files.
The following implements a merge like scheme for lists in python using objects with tag flatten
which on-the-fly recurses into items which are lists and tagged toflatten
. Using these two tags
you can have YAML file:
l1: &x1 !toflatten
- 1
- 2
l2: &x2
- 3
- 4
m1: !flatten
- *x1
- *x2
- [5, 6]
- !toflatten [7, 8]
(the use of flow vs block style sequences is completely arbitrary and has no influence on the
loaded result).
When iterating over the items that are the value for key m1
this
"recurses" into the sequences tagged with toflatten
, but displays
other lists (aliased or not) as a single item.
One possible way with Python code to achieve that is:
import sys
from pathlib import Path
import ruamel.yaml
yaml = ruamel.yaml.YAML()
@yaml.register_class
class Flatten(list):
yaml_tag = u'!flatten'
def __init__(self, *args):
self.items = args
@classmethod
def from_yaml(cls, constructor, node):
x = cls(*constructor.construct_sequence(node, deep=True))
return x
def __iter__(self):
for item in self.items:
if isinstance(item, ToFlatten):
for nested_item in item:
yield nested_item
else:
yield item
@yaml.register_class
class ToFlatten(list):
yaml_tag = u'!toflatten'
@classmethod
def from_yaml(cls, constructor, node):
x = cls(constructor.construct_sequence(node, deep=True))
return x
data = yaml.load(Path('input.yaml'))
for item in data['m1']:
print(item)
which outputs:
1
2
[3, 4]
[5, 6]
7
8
As you can see you can see, in the sequence that needs flattening, you
can either use an alias to a tagged sequence or you can use a tagged
sequence. YAML doesn't allow you to do:
- !flatten *x2
, i.e. tag an
anchored sequence, as this would essentially make it into a different
datastructure.
Using explicit tags is IMO better than having some magic going on as
with YAML merge keys <<
. If nothing else you now have to go through
hoops if you happen to have a YAML file with a mapping that has a key
<<
that you don't want to act like a merge key, e.g. when you make a
mapping of C operators to their descriptions in English (or some other natural language).