comment référencer un «réglage» YAML ailleurs dans le même fichier YAML?

J’ai le YAML suivant:

paths: patha: /path/to/root/a pathb: /path/to/root/b pathc: /path/to/root/c 

Comment puis-je “normaliser” ceci, en supprimant /path/to/root/ des trois chemins, et en le définissant comme quelque chose comme:

 paths: root: /path/to/root/ patha: *root* + a pathb: *root* + b pathc: *root* + c 

De toute évidence c’est invalide, je viens de le faire. Quelle est la syntaxe réelle? Peut-on le faire?

Je ne pense pas que ce soit possible. Vous pouvez réutiliser “node” mais pas en partie.

 bill-to: &id001 given : Chris family : Dumars ship-to: *id001 

Ceci est parfaitement valide YAML et les champs given et la family sont réutilisés dans le bloc de ship-to . Vous pouvez réutiliser un nœud scalaire de la même manière, mais vous ne pouvez en aucun cas modifier ce qu’il contient et y append la dernière partie d’un chemin depuis YAML.

Si la répétition vous dérange beaucoup, je suggère de rendre votre application consciente de root propriété root et de l’append à chaque chemin qui semble relatif non absolu.

Oui, en utilisant des balises personnalisées. Exemple en Python, faisant que la balise !join Join rejette des chaînes dans un tableau:

 import yaml ## define custom tag handler def join(loader, node): seq = loader.construct_sequence(node) return ''.join([str(i) for i in seq]) ## register the tag handler yaml.add_constructor('!join', join) ## using your sample data yaml.load(""" paths: root: &BASE /path/to/root/ patha: !join [*BASE, a] pathb: !join [*BASE, b] pathc: !join [*BASE, c] """) 

Qui aboutit à:

 { 'paths': { 'patha': '/path/to/root/a', 'pathb': '/path/to/root/b', 'pathc': '/path/to/root/c', 'root': '/path/to/root/' } } 

Le tableau des arguments à !join peut avoir n’importe quel nombre d’éléments de n’importe quel type de données, à condition qu’ils puissent être convertis en chaîne, donc !join [*a, "/", *b, "/", *c] ce que vous attendez

Une autre façon de voir cela est d’utiliser simplement un autre champ.

 paths: root_path: &root val: /path/to/root/ patha: &a root_path: *root rel_path: a pathb: &b root_path: *root rel_path: b pathc: &c root_path: *root rel_path: c 

Définition YML:

 dir:  default: /home/data/in/  proj1: ${dir.default}p1  proj2: ${dir.default}p2  proj3: ${dir.default}p3 

Quelque part dans thymeleaf

 

Sortie: / home / data / dans / / home / data / in / p1

J’ai créé une bibliothèque, disponible sur Packagist, qui remplit cette fonction: https://packagist.org/packages/grasmash/yaml-expander

Exemple de fichier YAML:

 type: book book: title: Dune author: Frank Herbert copyright: ${book.author} 1965 protaganist: ${characters.0.name} media: - hardcover characters: - name: Paul Atreides occupation: Kwisatz Haderach aliases: - Usul - Muad'Dib - The Preacher - name: Duncan Idaho occupation: Swordmaster summary: ${book.title} by ${book.author} product-name: ${${type}.title} 

Exemple de logique:

 // Parse a yaml ssortingng directly, expanding internal property references. $yaml_ssortingng = file_get_contents("dune.yml"); $expanded = \Grasmash\YamlExpander\Expander::parse($yaml_ssortingng); print_r($expanded); 

Tableau résultant:

 array ( 'type' => 'book', 'book' => array ( 'title' => 'Dune', 'author' => 'Frank Herbert', 'copyright' => 'Frank Herbert 1965', 'protaganist' => 'Paul Atreides', 'media' => array ( 0 => 'hardcover', ), ), 'characters' => array ( 0 => array ( 'name' => 'Paul Atreides', 'occupation' => 'Kwisatz Haderach', 'aliases' => array ( 0 => 'Usul', 1 => 'Muad\'Dib', 2 => 'The Preacher', ), ), 1 => array ( 'name' => 'Duncan Idaho', 'occupation' => 'Swordmaster', ), ), 'summary' => 'Dune by Frank Herbert', ); 

Dans certaines langues, vous pouvez utiliser une autre bibliothèque. Par exemple, tampax est une implémentation de variables de gestion YAML:

 const tampax = require('tampax'); const yamlSsortingng = ` dude: name: Arthur weapon: favorite: Excalibur useless: knife sentence: "{{dude.name}} use {{weapon.favorite}}. The goal is {{goal}}."`; const r = tampax.yamlParseSsortingng(yamlSsortingng, { goal: 'to kill Mordred' }); console.log(r.sentence); // output : "Arthur use Excalibur. The goal is to kill Mordred." 

Que votre exemple est invalide, c’est uniquement parce que vous avez choisi un caractère réservé pour démarrer vos scalaires. Si vous remplacez le * par un autre caractère non réservé (j’ai tendance à utiliser des caractères non-ASCII car ils sont rarement utilisés dans certaines spécifications), vous obtenez YAML parfaitement légal:

 paths: root: /path/to/root/ patha: ♦root♦ + a pathb: ♦root♦ + b pathc: ♦root♦ + c 

Cela va charger dans la représentation standard des mappages dans la langue utilisée par votre parsingur et ne pas étendre magiquement quoi que ce soit.
Pour ce faire, utilisez un type d’object par défaut comme dans le programme Python suivant:

 # coding: utf-8 from __future__ import print_function import ruamel.yaml as yaml class Paths: def __init__(self): self.d = {} def __repr__(self): return repr(self.d).replace('ordereddict', 'Paths') @staticmethod def __yaml_in__(loader, data): result = Paths() loader.construct_mapping(data, result.d) return result @staticmethod def __yaml_out__(dumper, self): return dumper.represent_mapping('!Paths', self.d) def __getitem__(self, key): res = self.d[key] return self.expand(res) def expand(self, res): try: before, rest = res.split(u'♦', 1) kw, rest = rest.split(u'♦ +', 1) rest = rest.lssortingp() # ssortingp any spaces after "+" # the lookup will throw the correct keyerror if kw is not found # recursive call expand() on the tail if there are multiple # parts to replace return before + self.d[kw] + self.expand(rest) except ValueError: return res yaml_str = """\ paths: !Paths root: /path/to/root/ patha: ♦root♦ + a pathb: ♦root♦ + b pathc: ♦root♦ + c """ loader = yaml.RoundTripLoader loader.add_constructor('!Paths', Paths.__yaml_in__) paths = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)['paths'] for k in ['root', 'pathc']: print(u'{} -> {}'.format(k, paths[k])) 

qui imprimera:

 root -> /path/to/root/ pathc -> /path/to/root/c 

Le développement se fait à la volée et gère les définitions nestedes, mais vous devez faire attention à ne pas invoquer une récursion infinie.

En spécifiant le dumper, vous pouvez vider le fichier YAML d’origine à partir des données chargées, en raison de l’extension à la volée:

 dumper = yaml.RoundTripDumper dumper.add_representer(Paths, Paths.__yaml_out__) print(yaml.dump(paths, Dumper=dumper, allow_unicode=True)) 

Cela changera le classement des clés de mappage. Si c’est un problème, vous devez créer un self.d CommentedMap (importé de ruamel.yaml.comments.py )