Existe-t-il une syntaxe YAML pour partager une partie d'une liste ou d'une carte?

94

Donc, je sais que je peux faire quelque chose comme ça:

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

anotherlist: *sites

Et ont sitelistet anotherlistcontiennent tous les deux www.foo.comet www.bar.com. Cependant, ce que je veux vraiment est pour anotherlistà également contenir www.baz.com, sans avoir à répéter www.foo.cometwww.baz.com .

Cela me donne une erreur de syntaxe dans l'analyseur YAML:

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

anotherlist: *sites
  - www.baz.com

En utilisant simplement des ancres et des alias, il ne semble pas possible de faire ce que je veux sans ajouter un autre niveau de sous-structure, tel que:

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

anotherlist:
  - *sites
  - www.baz.com

Ce qui signifie que le consommateur de ce fichier YAML doit en être conscient.

Existe-t-il une manière pure YAML de faire quelque chose comme ça? Ou vais-je devoir utiliser un traitement post-YAML, comme l'implémentation de la substitution de variables ou la levée automatique de certains types de sous-structures? Je fais déjà ce genre de post-traitement pour gérer quelques autres cas d'utilisation, donc je ne suis pas totalement opposé à cela. Mais mes fichiers YAML vont être écrits par des humains, pas générés par la machine, donc je voudrais minimiser le nombre de règles qui doivent être mémorisées par mes utilisateurs en plus de la syntaxe YAML standard.

J'aimerais aussi pouvoir faire la chose analogue avec les cartes:

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

moresites: *sites
  Baz: www.baz.com

J'ai fait une recherche dans la spécification YAML et je n'ai rien trouvé, donc je soupçonne que la réponse est simplement "non, vous ne pouvez pas faire ça". Mais si quelqu'un a des idées, ce serait génial.


EDIT: Puisqu'il n'y a eu aucune réponse, je suppose que personne n'a repéré quoi que ce soit que je n'ai pas dans la spécification YAML et que cela ne peut pas être fait au niveau de la couche YAML. J'ouvre donc la question à l'idée de post-traitement du YAML pour aider avec cela, au cas où quelqu'un trouverait cette question à l'avenir.

Ben
la source
Remarque: ce problème peut également être résolu avec l'utilisation standard des ancres et des alias dans YAML. Voir aussi: Comment fusionner des tableaux YAML?
dreftymac

Réponses:

53

Le type de clé de fusion est probablement ce que vous voulez. Il utilise un spécial<< clé de mappage pour indiquer les fusions, permettant à un alias d'un mappage (ou d'une séquence de tels alias) d'être utilisé comme initialiseur pour fusionner en un seul mappage. En outre, vous pouvez toujours remplacer explicitement les valeurs ou en ajouter d'autres qui n'étaient pas présentes dans la liste de fusion.

Il est important de noter que cela fonctionne avec des mappages, pas des séquences comme premier exemple. Cela a du sens quand vous y réfléchissez, et votre exemple semble ne pas avoir besoin d'être séquentiel de toute façon. Changer simplement vos valeurs de séquence en clés de mappage devrait faire l'affaire, comme dans l'exemple suivant (non testé):

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

Quelques choses à remarquer. Premièrement, puisqu'il <<s'agit d'une clé, elle ne peut être spécifiée qu'une seule fois par nœud. Deuxièmement, lorsque vous utilisez une séquence comme valeur, l'ordre est significatif. Cela n'a pas d'importance dans l'exemple ici, car il n'y a pas de valeurs associées, mais cela vaut la peine d'être conscient.

Kittemon
la source
Ah merci! C'est assez utile. C'est dommage que cela ne fonctionne pas pour les séquences, cependant. Vous avez raison, l'ordre n'est pas important pour cet exemple; ce que j'ai est conceptuellement un ensemble, mais qui correspond beaucoup plus à une séquence qu'à un mapping. Et la structure de ce que j'en retire compte (c'est pourquoi je ne voulais pas simplement ajouter une autre couche d'imbrication pour fusionner mes structures), donc avoir un mappage pour lequel je dois ignorer les valeurs (toutes nulles) ne suffit pas ça marche vraiment.
Ben
3
Je ne vois rien là-dessus dans la spécification YAML officielle actuelle: yaml.org/spec/1.2/spec.html . Cette page ne contient ni le mot «fusion», ni le texte «<<», ni l'expression «type de clé». La syntaxe << fonctionne cependant dans le package Python yaml. Savez-vous où je peux en savoir plus sur ces types de fonctionnalités supplémentaires?
Ben
1
Ce n'est pas directement dans la spécification, c'est décrit dans le référentiel de balises. Autres schémas a une description générale et un lien. Outre les clés de fusion, il existe également des ensembles et des ensembles ordonnés; cependant, YAML considère les ensembles comme un type de mappage (par exemple, l'exemple ci-dessus pourrait être implémenté comme un ensemble). Votre langue vous permet-elle d'échanger des clés avec des valeurs dans le mappage résultant? Même si vous devez mettre cela en œuvre vous-même, je pense que ce serait plus propre; vous auriez au moins déjà regroupé toutes les données et votre YAML serait standard.
kittemon
Les ensembles ne sont cependant pas des mappages; un mappage est un ensemble d'associations clé-valeur. Quand je suis yaml.load(...)en Python, j'obtiens un dictionnaire comme représentation d'un mappage YAML. Oui, il est facile de post-traiter cela dans un ensemble, mais je dois savoir que cela s'est produit (et la complexité sémantique lors de la lecture / écriture des fichiers de configuration est beaucoup plus élevée si la règle est "les ensembles sont écrits comme des cartes avec des valeurs nulles" ). Étant donné que j'ai besoin d'un post-traitement entre yaml.load(...)et en utilisant les données résultantes, que j'utilise <<ou MERGE, je m'en tiendrai probablement MERGE(ce que j'ai déjà implémenté maintenant).
Ben
2
Ouais, j'ai trouvé que ça !!setmarche. Trop de passe-partout obscur cependant. Ces fichiers sont conçus pour être lisibles / inscriptibles par l'homme, par des personnes qui ne sont pas nécessairement des experts YAML. Les gens vont écrire leurs listes de sites sous forme de listes YAML, puis veulent les fusionner et doivent convertir le tout en un ensemble ET n'oubliez pas de le marquer explicitement comme un ensemble ... J'ai quelques autres articles standardisés. traitement des choses avec de MERGEtoute façon. Merci quand même pour vôtre aide!
Ben
16

Comme les réponses précédentes l'ont souligné, il n'y a pas de support intégré pour l'extension des listes dans YAML. Je propose encore une autre façon de le mettre en œuvre vous-même. Considère ceci:

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

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

Cela sera traité en:

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

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

L'idée est de fusionner le contenu d'une clé se terminant par un '+' à la clé correspondante sans un '+'. J'ai implémenté cela en Python et publié ici .

Prendre plaisir!

Alexander Ryzhov
la source
2
Remarque: ce problème peut également être résolu avec l'utilisation standard des ancres et des alias dans YAML. Voir aussi: Comment fusionner des tableaux YAML?
dreftymac
11
Cela signifie-t-il que cette approche ne fonctionne qu'avec un outil distinct qui fusionne siteset sites+. Je veux dire un outil qui doit être implémenté par l'utilisateur car ce n'est pas un yamlcomportement par défaut ?
stan0
7

(Répondre à ma propre question au cas où la solution que j'utilise serait utile à quiconque la recherche à l'avenir)

En l'absence de moyen purement YAML de le faire, je vais implémenter cela comme une «transformation de syntaxe» entre l'analyseur YAML et le code qui utilise réellement le fichier de configuration. Ainsi, mon application principale n'a pas du tout à se soucier des mesures d'évitement de redondance conviviales pour l'homme, et peut simplement agir directement sur les structures résultantes.

La structure que je vais utiliser ressemble à ceci:

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

Ce qui serait transformé en l'équivalent de:

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

Ou, avec des cartes:

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

Serait transformé en:

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

Plus formellement, après avoir appelé l'analyseur YAML pour obtenir des objets natifs à partir d'un fichier de configuration, mais avant de passer les objets au reste de l'application, mon application parcourra le graphe d'objets à la recherche de mappages contenant la clé unique MERGE. La valeur associée à MERGEdoit être soit une liste de listes, soit une liste de cartes; toute autre sous-structure est une erreur.

Dans le cas des listes de listes, la carte entière contenant MERGEsera remplacée par les listes enfants concaténées ensemble dans l'ordre d'apparition.

Dans le cas de la liste de mappes, la mappe entière contenant MERGEsera remplacée par une mappe unique contenant toutes les paires clé / valeur des mappes enfants. En cas de chevauchement des clés, la valeur de la carte enfant apparaissant en dernierMERGE liste sera utilisée.

Les exemples donnés ci-dessus ne sont pas très utiles, car vous auriez pu simplement écrire la structure que vous vouliez directement. Il est plus susceptible d'apparaître comme:

foo:
  MERGE:
    - *salt
    - *pepper

Vous permettant de créer une liste ou une carte contenant tout ce qui se trouve dans les nœuds saltet pepperutilisée ailleurs.

(Je continue de donner cette foo:carte externe pour montrer que MERGEdoit être la seule clé de son mappage, ce qui signifie que MERGEcela ne peut pas apparaître comme un nom de niveau supérieur à moins qu'il n'y ait pas d'autres noms de niveau supérieur)

Ben
la source
6

Pour clarifier quelque chose à partir des deux réponses ici, cela n'est pas pris en charge directement dans YAML pour les listes (mais il est pris en charge pour les dictionnaires, voir la réponse de kittemon).

asmeureur
la source
Remarque: ce problème peut également être résolu avec l'utilisation standard des ancres et des alias dans YAML. Voir aussi: Comment fusionner des tableaux YAML?
dreftymac
5

Pour utiliser la réponse de Kittemon, notez que vous pouvez créer des mappages avec des valeurs nulles en utilisant la syntaxe alternative

foo:
    << : myanchor
    bar:
    baz:

au lieu de la syntaxe suggérée

foo:
    << : myanchor
    ? bar
    ? baz

Comme la suggestion de Kittemon, cela vous permettra d'utiliser des références aux ancres dans le mappage et d'éviter le problème de séquence. Je me suis retrouvé à devoir le faire après avoir découvert que le composant Symfony Yaml v2.4.4 ne reconnaissait pas la ? barsyntaxe.

boeuf_booléen
la source
à quoi ça myanchorressemble?
ssc