Ça ne va pas marcher:
la fusion n'est prise en charge que par les spécifications YAML pour les mappages et non pour les séquences
vous mélangez complètement les choses en ayant une clé de fusion <<
suivie du séparateur clé / valeur :
et une valeur qui est une référence , puis continuez avec une liste au même niveau d'indentation
Ce n'est pas correct YAML:
combine_stuff:
x: 1
- a
- b
Ainsi, votre exemple de syntaxe n'aurait même pas de sens en tant que proposition d'extension YAML.
Si vous souhaitez faire quelque chose comme la fusion de plusieurs tableaux, vous pouvez envisager une syntaxe telle que:
combined_stuff:
- <<: *s1, *s2
- <<: *s3
- d
- e
- f
où s1
, s2
, s3
sont des ancres sur des séquences (non représentés) que vous souhaitez fusionner dans une nouvelle séquence, puis ont la d
, e
et f
annexées à cela. Mais YAML résout d'abord ce type de profondeur de structures, il n'y a donc pas de contexte réel disponible pendant le traitement de la clé de fusion. Il n'y a pas de tableau / liste à votre disposition où vous pouvez attacher la valeur traitée (la séquence ancrée).
Vous pouvez adopter l'approche proposée par @dreftymac, mais cela présente l'énorme inconvénient que vous devez en quelque sorte connaître les séquences imbriquées à aplatir (c'est-à-dire en connaissant le "chemin" de la racine de la structure de données chargée à la séquence parente), ou que vous parcouriez récursivement la structure de données chargée à la recherche de tableaux / listes imbriqués et de les aplatir tous sans discernement.
Une meilleure solution IMO serait d'utiliser des balises pour charger des structures de données qui font l'aplatissement pour vous. Cela permet d'indiquer clairement ce qui doit être aplati et ce qui ne l'est pas et vous donne un contrôle total sur si cet aplatissement est effectué pendant le chargement ou pendant l'accès. Laquelle choisir est une question de facilité de mise en œuvre et d'efficacité en termes de temps et d'espace de stockage. C'est le même compromis qui doit être fait pour implémenter la fonctionnalité de clé de fusion et il n'y a pas de solution unique qui soit toujours la meilleure.
Par exemple, ma ruamel.yaml
bibliothèque utilise les merge-dicts de force brute lors du chargement lors de l'utilisation de son chargeur sécurisé, ce qui entraîne des dictionnaires fusionnés qui sont des dictionnaires Python normaux. Cette fusion doit être effectuée à l'avance et duplique les données (espace inefficace) mais est rapide dans la recherche de valeur. Lorsque vous utilisez le chargeur aller-retour, vous voulez pouvoir vider les fusions non fusionnées, elles doivent donc être séparées. Le dict, comme la structure de données chargée à la suite du chargement aller-retour, est peu encombrant mais plus lent d'accès, car il doit essayer de rechercher une clé qui ne se trouve pas dans le dict lui-même dans les fusions (et cela n'est pas mis en cache, donc il doit être fait à chaque fois). Bien entendu, ces considérations ne sont pas très importantes pour les fichiers de configuration relativement petits.
Ce qui suit implémente un schéma de fusion pour les listes en python en utilisant des objets avec une balise flatten
qui récursent à la volée dans des éléments qui sont des listes et des balises toflatten
. En utilisant ces deux balises, vous pouvez avoir un fichier YAML:
l1: &x1 !toflatten
- 1
- 2
l2: &x2
- 3
- 4
m1: !flatten
- *x1
- *x2
- [5, 6]
- !toflatten [7, 8]
(l'utilisation de séquences de style flux vs bloc est complètement arbitraire et n'a aucune influence sur le résultat chargé).
Lors de l'itération sur les éléments qui sont la valeur de la clé, m1
cela «revient» dans les séquences étiquetées avec toflatten
, mais affiche d'autres listes (aliasées ou non) comme un élément unique.
Un moyen possible avec le code Python d'y parvenir est:
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)
qui sort:
1
2
[3, 4]
[5, 6]
7
8
Comme vous pouvez le voir, dans la séquence à aplatir, vous pouvez soit utiliser un alias pour une séquence balisée, soit utiliser une séquence balisée. YAML ne vous permet pas de faire:
- !flatten *x2
, c'est-à-dire étiqueter une séquence ancrée, car cela en ferait essentiellement une structure de données différente.
L'utilisation de balises explicites est préférable à l'OMI que de faire de la magie comme avec les clés de fusion YAML <<
. Si rien d'autre, vous devez maintenant passer par des cerceaux si vous avez un fichier YAML avec un mappage qui a une clé
<<
que vous ne voulez pas agir comme une clé de fusion, par exemple lorsque vous faites un mappage des opérateurs C à leurs descriptions en anglais (ou dans une autre langue naturelle).