2010-01-14 7 views
73

je l'YAML suivante:comment référencer un "paramètre" YAML d'ailleurs dans le même fichier YAML?

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

Comment puis-je « normaliser » ce, en supprimant /path/to/root/ des trois chemins, et ont comme son propre cadre, quelque chose comme:

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

Il est évident que c'est invalide, je l'ai juste inventé. Quelle est la vraie syntaxe? Peut-il être fait?

+0

** Voir aussi: ** https://stackoverflow.com/a/41620747/42223 – dreftymac

Répondre

64

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

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

Ceci est parfaitement valable YAML et les champs given et family sont réutilisés dans ship-to bloc. Vous pouvez réutiliser un nœud scalaire de la même manière, mais vous ne pouvez en aucun cas modifier ce qui est à l'intérieur et y ajouter la dernière partie d'un chemin depuis YAML.

Si la répétition vous dérange autant que je suggère de rendre votre application au courant de la propriété root et l'ajouter à chaque chemin qui semble relative non absolue.

+1

Ok merci, ouais je dois ajouter la racine du code. pas de biggie. –

+2

La réponse acceptée n'est pas exacte. Voir ma réponse pour une solution. –

+0

comment faire ceci, si ** bill-to ** est dans un autre fichier, que nous avons importé où ** ship-to ** est défini? –

37

Oui, en utilisant des balises personnalisées. Exemple en Python, ce qui rend la balise !join joindre les 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 se traduit par:

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

Le tableau d'arguments à !join peuvent avoir un certain nombre d'éléments de tout type de données, Tant qu'ils peuvent être convertis en chaîne, alors !join [*a, "/", *b, "/", *c] fait ce que vous attendez.

+1

J'aime ta solution, plus simple dans le codage que la mienne au prix d'un YAML un peu moins lisible. – Anthon

+0

Celui qui a voté cela - pouvez-vous dire pourquoi s'il vous plaît? –

+0

Le point culminant au-dessus de la flèche vers le bas dit "cette réponse n'est pas utile" et c'est essentiellement le cas pour quiconque n'est pas intéressé par le sujet de YAML. Pire encore personne (à part moi, parce que j'étais le seul intervenant jusqu'à présent) recevra une notification de votre commentaire/question. Les chances que quelqu'un qui est déprécié revient ici et lit votre commentaire et les réponses sont si bas que je considérerais ceux qui sont proches de l'inexistante. Cela dit, ce n'était pas moi, et si j'étais vous je ne demanderais pas et ne supprimerais pas le commentaire. – Anthon

7

Une autre façon de voir cela est simplement d'utiliser 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 
-1

que votre exemple est invalide est uniquement parce que vous avez choisi un caractère réservé pour commencer votre avec scalaires. Si vous remplacez le * avec un autre caractère non réservé (j'ai tendance à utiliser des caractères non-ASCII pour que, comme ils sont rarement utilisés dans le cadre de certaines spécifications), vous vous retrouvez avec 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 pour les mappages dans la langue que votre analyseur utilise et ne développe rien magiquement.
Pour ce faire utilisez un type d'objet par défaut localement 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.lstrip() # strip 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 

L'expansion se fait à la volée et les poignées définitions imbriquées, mais vous doivent faire attention à ne pas invoquer la récursion infinie.

En spécifiant le dumper, vous pouvez vider le YAML d'origine à partir des données chargées, à cause de la sur la volée extension:

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

cela va changer l'ordre des clés de la cartographie. Si tel est un problème que vous avez à faire self.d un CommentedMap (importé de ruamel.yaml.comments.py)

1

J'ai créer 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} 

logique Exemple:

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

réseau 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', 
); 
+0

Aimer le concept de l'expandeur! –

2

définition YML:

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

Quelque part dans thymeleaf

<p th:utext='${@environment.getProperty("dir.default")}' /> 
<p th:utext='${@environment.getProperty("dir.proj1")}' /> 

sortie: /home/data/dans/ /home/data/dans/p1

+0

@AndrewBullock Je pense que cela devrait être la réponse acceptée, car elle résout exactement votre problème. –

+1

Non, ce n'est pas une utilisation native de variable dans YAML et elle n'est spécifiée dans aucune version de spécification. Après un test, cela ne fonctionne pas. –

+0

Cela a probablement fonctionné pour Pavol en utilisant quelque chose qui a prétraité le yaml (c'est-à-dire le filtrage du plugin maven-resources-plugin) – DeezCashews

0

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

const tampax = require('tampax'); 

const yamlString = ` 
dude: 
    name: Arthur 
weapon: 
    favorite: Excalibur 
    useless: knife 
sentence: "{{dude.name}} use {{weapon.favorite}}. The goal is {{goal}}."`; 

const r = tampax.yamlParseString(yamlString, { goal: 'to kill Mordred' }); 
console.log(r.sentence); 

// output : "Arthur use Excalibur. The goal is to kill Mordred."