Question

Je le YAML suivant:

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 viens de l'inventer. Quelle est la syntaxe réelle? Peut-il être fait?

Était-ce utile?

La solution

Je ne pense pas qu'il soit possible. Vous pouvez réutiliser « noeud », mais ne fait pas partie de celui-ci.

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

Ceci est parfaitement YAML valide et les champs given et family sont réutilisés dans le bloc ship-to. Vous pouvez réutiliser un nœud scalaire de la même manière, mais il n'y a aucun moyen que vous pouvez changer ce qui est à l'intérieur et ajouter cette dernière partie d'un chemin d'accès à l'intérieur YAML.

Si la répétition vous dérange pas tant que ça, je vous suggère de faire votre demande au courant des biens root et l'ajouter à chaque chemin qui semble relativement pas absolue.

Autres conseils

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]
""")

Ce 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 peut avoir un certain nombre d'éléments de tout type de données, aussi longtemps qu'ils peuvent être convertis en chaîne, donc !join [*a, "/", *b, "/", *c] fait ce que vous attendez.

Une autre façon de regarder 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

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

Sortie: / Home / data / dans / / Home / data / dans / p1

Dans certaines langues, vous pouvez utiliser une autre bibliothèque, par exemple, Tampax est une implémentation de variables de manipulation 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."

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}

Exemple logique:

// 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',
);

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 correspondances dans la langue de votre analyseur utilise et ne se développe pas comme par magie quoi que ce soit.
Pour ce faire, comme dans le programme Python suivant qui utilisent un type d'objet par défaut localement:

# 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

Le expand est fait à la volée et les poignées définitions imbriquées, mais vous devez faire attention à ne pas invoquer une 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)

Je l'ai écrit ma propre bibliothèque Python pour développer les variables étant chargées à partir des répertoires avec une hiérarchie comme:

/root
 |
 +- /proj1
     |
     +- config.yaml
     |
     +- /proj2
         |
         +- config.yaml
         |
         ... and so on ...

La principale différence ici est que l'expansion doit être appliquée seulement après que tous les fichiers config.yaml est chargé, où les variables à partir du fichier suivant peuvent remplacer les variables de la précédente, de sorte que le pseudo-code devrait ressembler à ceci:

env = YamlEnv()
env.load('/root/proj1/config.yaml')
env.load('/root/proj1/proj2/config.yaml')
...
env.expand()

En option supplémentaire le script xonsh peut exporter les variables obtenues dans les variables d'environnement (voir la fonction yaml_update_global_vars).

Les scripts:

https: // sourceforge .net / p / contools / contools / HEAD / arbre / tronc / Scripts / Outils / cmdoplib.yaml.py https://sourceforge.net/ p / contools / contools / HEAD / arbre / tronc / Scripts / Outils / cmdoplib.yaml.xsh

Plus :

  • simple, ne supporte pas les variables récursivité et imbriquées
  • peut remplacer une variable non définie à un espace réservé (${MYUNDEFINEDVAR} -> *$/{MYUNDEFINEDVAR})
  • peut développer une référence de variable d'environnement (${env:MYVAR})
  • peut remplacer tout \\ à / dans une variable de chemin (${env:MYVAR:path})

Inconvénients :

  • ne supporte pas les variables imbriquées, ne peut donc pas développer des valeurs dans les dictionnaires imbriqués (quelque chose comme ${MYSCOPE.MYVAR} n'est pas mis en œuvre)
  • ne détecte pas la récursivité d'expansion, y compris récursion après un espace réservé mis
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top