syntax - cómo hacer referencia a una "configuración" de YAML desde otro lugar en el mismo archivo YAML?
transclusion (7)
Creé una biblioteca, disponible en Packagist, que realiza esta función: https://packagist.org/packages/grasmash/yaml-expander
Ejemplo de archivo 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}
Ejemplo de lógica:
// 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);
Matriz 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'',
);
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, eliminando /path/to/root/
desde las tres rutas, y tenerlo como su propia configuración, algo así como:
paths:
root: /path/to/root/
patha: *root* + a
pathb: *root* + b
pathc: *root* + c
Obviamente eso no es válido, lo inventé. ¿Cuál es la sintaxis real? Se puede hacer?
En algunos idiomas, puede usar una biblioteca alternativa. Por ejemplo, tampax es una implementación de 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."
No creo que sea posible. Puede reutilizar "nodo" pero no parte de él.
bill-to: &id001
given : Chris
family : Dumars
ship-to: *id001
Este es un YAML perfectamente válido y los campos given
y la family
se reutilizan en el ship-to
bloque. Puede reutilizar un nodo escalar de la misma manera, pero no hay forma de que pueda cambiar lo que está dentro y agregar la última parte de una ruta desde dentro de YAML.
Si la repetición te molesta tanto, te sugiero que hagas que tu aplicación conozca root
propiedad root
y la agregue a cada ruta que se ve como relativa, no absoluta.
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
Que su ejemplo no sea válido es solo porque eligió un carácter reservado para comenzar sus escalares. Si reemplaza *
con algún otro carácter no reservado (tiendo a usar caracteres que no sean ASCII para eso, ya que rara vez se usan como parte de alguna especificación), termina con YAML perfectamente legal:
paths:
root: /path/to/root/
patha: ♦root♦ + a
pathb: ♦root♦ + b
pathc: ♦root♦ + c
Esto se cargará en la representación estándar para asignaciones en el lenguaje que utiliza su analizador sintáctico y no expande mágicamente nada.
Para hacer eso, use un tipo de objeto localmente predeterminado 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 se imprimirá:
root -> /path/to/root/
pathc -> /path/to/root/c
La expansión se realiza sobre la marcha y maneja definiciones anidadas, pero debe tener cuidado al no invocar recursión infinita.
Al especificar el volcador, puede volcar el YAML original de los datos cargados, debido a la expansión sobre la marcha:
dumper = yaml.RoundTripDumper
dumper.add_representer(Paths, Paths.__yaml_out__)
print(yaml.dump(paths, Dumper=dumper, allow_unicode=True))
esto cambiará el orden de las claves de mapeo. Si eso es un problema, debe hacer self.d
a CommentedMap
(importado de ruamel.yaml.comments.py
)
Sí, usando etiquetas personalizadas Ejemplo en Python, haciendo que la etiqueta !join
Join se una a las 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]
""")
Lo que resulta en:
{
''paths'': {
''patha'': ''/path/to/root/a'',
''pathb'': ''/path/to/root/b'',
''pathc'': ''/path/to/root/c'',
''root'': ''/path/to/root/''
}
}
La matriz de argumentos para !join
puede tener cualquier cantidad de elementos de cualquier tipo de datos, siempre que se puedan convertir en cadena, así que !join [*a, "/", *b, "/", *c]
no lo que esperarías
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 la hoja de tomillo
<p th:utext=''${@environment.getProperty("dir.default")}'' />
<p th:utext=''${@environment.getProperty("dir.proj1")}'' />
Salida: / home / data / in / / home / data / in / p1