web-dev-qa-db-fra.com

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

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

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

anotherlist: *sites

Et que sitelist et anotherlist contiennent tous deux www.foo.com et www.bar.com. Cependant, ce que je veux vraiment, c'est que anotherlist à aussi contient www.baz.com, sans avoir à répéter www.foo.com et www.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 YAML pure de faire quelque chose comme ça? Ou devrai-je utiliser un traitement post-YAML, comme l'implémentation de la substitution de variables ou le levage automatique de certains types de sous-structure? Je fais déjà ce genre de post-traitement pour gérer quelques autres cas d'utilisation, donc je n'y suis pas totalement opposé. Mais mes fichiers YAML vont être écrits par des humains, pas générés par 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 même chose 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 pense 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 pas eu de réponse, je suppose que personne n'a repéré quelque chose que je n'ai pas dans la spécification YAML et que cela peut ' t être fait à la couche YAML. J'ouvre donc la question à l'idée d'un post-traitement du YAML pour aider à cela, au cas où quelqu'un trouverait cette question à l'avenir.

77
Ben

Le type de clé de fusion est probablement ce que vous voulez. Il utilise un << clé de mappage pour indiquer les fusions, permettant à un alias d'un mappage (ou à une séquence de ces 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 lorsque vous y pensez, et votre exemple semble qu'il n'a probablement pas 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. Tout d'abord, puisque << est une clé, elle ne peut être spécifiée qu'une seule fois par nœud. Deuxièmement, lors de l'utilisation d'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.

49
kittemon

Comme les réponses précédentes l'ont souligné, il n'y a pas de prise en charge intégrée 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 transformé 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 "+". J'ai implémenté cela dans Python et publié ici .

Prendre plaisir!

16
Alexander Ryzhov

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

6
asmeurer

Pour vous baser sur la réponse de Kittemon, notez que vous pouvez créer des mappages avec des valeurs nulles à l'aide de 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é à le faire après avoir découvert que le composant Symfony Yaml v2.4.4 ne reconnait pas le ? bar syntaxe.

4
beef_boolean

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

Sans moyen purement YAML de le faire, je vais implémenter cela comme une "transformation de syntaxe" située entre l'analyseur YAML et le code qui utilise réellement le fichier de configuration. Mon application principale n'a donc pas à se soucier du tout des mesures d'évitement de la redondance respectueuses de 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

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 des objets à la recherche de mappages contenant la clé unique MERGE. La valeur associée à MERGE doit être soit une liste de listes, soit une liste de cartes; toute autre sous-structure est une erreur.

Dans le cas de la liste de listes, la carte entière contenant MERGE sera remplacée par les listes enfants concaténées ensemble dans l'ordre où elles sont apparues.

Dans le cas de la liste des cartes, la carte entière contenant MERGE sera remplacée par une seule carte contenant toutes les paires clé/valeur dans les cartes enfants. En cas de chevauchement dans les clés, la valeur de la carte enfant apparaissant en dernier dans la liste MERGE sera utilisée.

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

foo:
  MERGE:
    - *salt
    - *pepper

Vous permettant de créer une liste ou une carte contenant tout dans les nœuds salt et pepper utilisés ailleurs.

(Je continue de donner ça foo: carte externe pour montrer que MERGE doit être la clé seulement dans son mappage, ce qui signifie que MERGE ne peut pas apparaître comme un nom de niveau supérieur à moins qu'il n'y ait autres noms de premier niveau)

4
Ben