Pergunta

Eu tenho o seguinte YAML:

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

Como eu posso "normalizar" este, removendo /path/to/root/ a partir de três caminhos, e tê-lo como sua própria definição, algo como:

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

Obviamente, é inválido, eu só fiz isso.O que é a sintaxe?Isso pode ser feito?

Foi útil?

Solução

Eu não acho que seja possível. Você pode reutilizar o "nó", mas não fazer parte dele.

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

Este é o YAML e os campos perfeitamente válidos given e family são reutilizados ship-to quadra. Você pode reutilizar um nó escalar da mesma maneira, mas não há como mudar o que está dentro e adicionar a última parte de um caminho a partir dele de dentro da YAML.

Se a repetição o incomoda, sugiro que seu aplicativo consciente root propriedade e adicione -a a todos os caminhos que parecem relativos não absolutos.

Outras dicas

Sim, usando tags personalizadas. Exemplo em python, fazendo o !join Tag Junt Strings em uma 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]
""")

O que resulta em:

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

A variedade de argumentos para !join pode ter qualquer número de elementos de qualquer tipo de dados, desde que possam ser convertidos em string, então !join [*a, "/", *b, "/", *c] faz o que você esperaria.

Outra maneira de olhar para isso é simplesmente usar outro 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

Definição YML:

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

Em algum lugar em Thymeleaf

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

Resultado:/home/dados/in//home/data/in/p1

Em alguns idiomas, você pode usar uma alternativa biblioteca, Por exemplo, tampax é uma implementação de YAML manipulação de variáveis:

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."

Eu criei uma biblioteca, disponível no Packagist, que executa esta função:https://packagist.org/packages/gramashash/yaml-expander

Exemplo de arquivo 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}

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

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 o seu exemplo é inválido é apenas porque você escolheu um caractere reservado para iniciar o seu escalares com.Se você substituir o * com alguns outros reservada não caractere (eu costumo usar caracteres não-ASCII, para que eles raramente são usados como parte de alguns especificação), pode acabar com perfeitamente legal YAML:

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

Isto irá carregar para o padrão de representação para mapeamentos em a linguagem que o analisador usa e não magicamente expanda nada.
Para isso use uma localmente padrão de tipo de objeto, como no seguinte programa 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]))

o que irá imprimir:

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

A expansão é feita na altura e alças aninhadas definições, mas você tem que ter cuidado de não invocar a recursividade infinita.

Especificando o dumper, você pode copiar o original YAML a partir dos dados carregados, por causa do on-the-fly de expansão:

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

isso irá alterar o mapeamento de chave de ordenação.Se isso é um problema, você tem para fazer self.d um CommentedMap (importados de ruamel.yaml.comments.py)

Eu escrevi a minha própria biblioteca em Python para expandir variáveis a ser carregado a partir de diretórios com uma hierarquia:

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

A principal diferença aqui é que a expansão deve ser aplicado somente depois que todos os config.yaml arquivos é carregado, onde as variáveis a partir do próximo arquivo pode substituir as variáveis da anterior, de modo que o pseudocódigo deve olhar como este:

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

Como uma opção adicional que o xonsh script pode exportar resultante de variáveis em variáveis de ambiente (ver o yaml_update_global_vars função).

Os scripts:

https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools/cmdoplib.yaml.py https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts/Tools/cmdoplib.yaml.xsh

Prós:

  • simples, não suporta a recursividade e aninhados variáveis
  • pode substituir uma variável indefinida a um marcador de posição (${MYUNDEFINEDVAR} -> *$/{MYUNDEFINEDVAR})
  • pode expandir uma referência a partir da variável de ambiente (${env:MYVAR})
  • pode substituir todas as \\ para / em uma variável de caminho (${env:MYVAR:path})

Contras:

  • não oferece suporte a aninhadas variáveis, portanto, não pode expandir valores em aninhadas dicionários (algo como ${MYSCOPE.MYVAR} não está implementado)
  • não detecta expansão recursividade, incluindo recursão depois de um espaço reservado para colocar
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top