Pregunta

Tengo el siguiente YAML:

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

¿Cómo puedo "normalizar" esto, mediante la eliminación de /path/to/root/ de los tres caminos, y lo tienen como su propio ajuste, algo como:

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

Obviamente eso es inválida, me acabo de inventar. ¿Cuál es la sintaxis real? ¿Se puede hacer?

¿Fue útil?

Solución

No creo que es posible. Puede volver a utilizar "nodo", pero no forma parte de ella.

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

Esto es perfectamente válido y YAML campos given y family se reutilizan en el bloque ship-to. Puede volver a utilizar un nodo escalar la misma manera pero no hay manera de que pueda cambiar lo que está dentro y añadir la última parte de un camino a la misma desde el interior YAML.

Si la repetición se molestó mucho Propongo hacer su aplicación consciente de la propiedad root y añadirlo a cada camino que se ve relativa no absoluta.

Otros consejos

Sí, el uso de etiquetas personalizadas. Ejemplo en Python, haciendo que la etiqueta !join unirse cadenas en una matriz:

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

que se traduce en:

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

El array de parámetros a !join puede tener cualquier número de elementos de cualquier tipo de datos, siempre y cuando se pueden convertir en cadena, por lo !join [*a, "/", *b, "/", *c] hace lo que se puede esperar.

Otra forma de ver esto es simplemente usar otro campo.

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

definición YML:

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

En algún lugar de thymeleaf

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

Salida: / Home / datos / en / / Home / datos / en / p1

En algunos idiomas, puede utilizar una biblioteca alternativa, por ejemplo, Tampax es una implementación de las variables de manejo 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."

He crear una biblioteca, disponible en Packagist, que realiza esta función: https://packagist.org/packages/grasmash/yaml-expander

archivo

Ejemplo 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}

lógica Ejemplo:

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

array resultante:

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 su ejemplo no es válido es Sólo ya ha elegido un carácter reservado para comenzar sus escalares con. Si reemplaza el * con algún otro carácter no reservado (que tienden a utilizar caracteres no ASCII para eso, ya que rara vez se utilizan como parte de alguna especificación), se termina con YAML perfectamente legal:

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

Esto cargará en la representación estándar para asignaciones en el idioma de su analizador usa y no se expande mágicamente nada.
Para hacerlo use un tipo de objeto por defecto a nivel local como en el siguiente programa de Python:

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

que imprimirá:

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

La expansión se realiza sobre la marcha y se ocupa de las definiciones anidadas, pero hay que tener cuidado con no invocar la recursividad infinita.

Al especificar el descargador, se puede volcar el YAML original a partir de los datos cargados en, debido a la expansión de la marcha:

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

esto va a cambiar el orden de asignación de claves. Si esto es un problema que tienes para hacer un self.d CommentedMap (importado de ruamel.yaml.comments.py)

he escrito mi propia biblioteca de Python para ampliar variables que están siendo cargados desde directorios con una jerarquía como:

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

La diferencia clave aquí es que la expansión debe aplicarse sólo después de que todos los archivos config.yaml se carga, donde las variables de la siguiente fichero pueden prevalecer sobre las variables de la anterior, por lo que el pseudocódigo debe tener este aspecto:

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

Como opción adicional la secuencia de comandos xonsh puede exportar las variables resultantes en las variables de entorno (véase la función yaml_update_global_vars).

Las secuencias de comandos:

https: // sourceforge .net / P / contools / contools / cabeza / árbol / tronco / Scripts / Herramientas / cmdoplib.yaml.py https://sourceforge.net/ p / contools / contools / cabeza / árbol / tronco / Scripts / Herramientas / cmdoplib.yaml.xsh

Pros

  • simple, no soporta la recursividad y las variables anidadas
  • puede sustituir a una variable no definida a un marcador de posición (${MYUNDEFINEDVAR} -> *$/{MYUNDEFINEDVAR})
  • puede expandir una referencia de variable de entorno (${env:MYVAR})
  • Puede recuperar todos los \\ a / en una variable de ruta (${env:MYVAR:path})

Contras

  • no soporta las variables anidadas, por lo que no se puede expandir valores en los diccionarios anidados (algo así como ${MYSCOPE.MYVAR} no está implementado)
  • no detecta la recursividad de expansión, incluyendo la repetición después de un marcador de posición puso
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top