2010-01-14 2 views
73

У меня есть следующий YAML:Как ссылаться на YAML «настройка» из другого места в том же файле YAML?

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

Как я могу «нормализовать» это путем удаления /path/to/root/ из трех путей, и иметь его в качестве своей собственной установки, что-то вроде:

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

Очевидно это недействительно, я просто сделал это. Каков реальный синтаксис? Это можно сделать?

+0

** См. Также: ** https://stackoverflow.com/a/41620747/42223 – dreftymac

ответ

64

Я не думаю, что это возможно. Вы можете повторно использовать «узел», но не часть его.

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

Это вполне допустимо YAML и поля given и family повторно используются в ship-to блоке. Вы можете повторно использовать скалярный узел таким же образом, но вы не можете изменить то, что внутри, и добавить эту последнюю часть пути к нему изнутри YAML.

Если повторение беспокоит вас так, я предлагаю сделать ваше приложение осведомленным о свойстве root и добавить его к каждому пути, который выглядит относительным, а не абсолютным.

+1

Хорошо, спасибо, да, надо добавить код «root». нет biggie. –

+2

Принятый ответ не является точным. См. Мой ответ для решения. –

+0

как это сделать, если ** bill-to ** находится в другом файле, который мы импортировали там, где определено ** ship-to **? –

37

Да, используя пользовательские теги. Пример в Python, что делает !join тег соединить строки в массиве:

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

Какие результаты в:

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

Массив аргументов !join может иметь любое количество элементов данных любого типа, а если они могут быть преобразованы в строку, поэтому !join [*a, "/", *b, "/", *c] делает то, что вы ожидаете.

+1

Мне нравится ваше решение, проще в кодировании, чем мое за счет чуть менее читаемого YAML. – Anthon

+0

Кто бы ни проголосовал за это - скажите, пожалуйста, пожалуйста? –

+0

Выделение над стрелкой вниз говорит, что «этот ответ не полезен», и это, по сути, относится к любому, кто не заинтересован в теме YAML. Еще хуже никого (кроме меня, потому что я был единственным комментатором до сих пор) получит уведомление о вашем комментарии/вопросе. Скорее всего, кто-то, кто остановился, возвращается сюда и читает ваши комментарии и ответы настолько низки, что я считаю, что они близки к несуществующим. Сказав это, это был не я, и если бы я был вами, я бы не спросил и не удалял комментарий. – Anthon

7

Другой способ взглянуть на это - просто использовать другое поле.

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

Это ваш пример неверен является только, потому что вы выбрали зарезервированный символ, чтобы начать свой скаляры с. Если заменить * с каким-либо другим незарезервированный характера (я предпочитаю использовать не-ASCII символы для этого, поскольку они редко используются как часть некоторой спецификации), вы в конечном итоге с совершенно законного YAML:

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

Это будет загружаться в стандартное представление для сопоставлений на языке, который использует ваш парсер, и не волшебным образом расширяет что-либо.
Для этого используется тип локально по умолчанию объекта как в следующей программе 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])) 

, который будет печатать:

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

расширяющейся делается на лету и обрабатывает вложенные определения, но должны быть осторожны, чтобы не вызвать бесконечную рекурсию.

Указав самосвал, вы можете сбросить первоначальную YAML из данных, загруженных в, из-за расширения на лету:

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

это изменит порядок ключей отображения. Если это проблема, вы должны сделать self.dCommentedMap (импорт из ruamel.yaml.comments.py)

1

Я создать библиотеку, доступную на Packagist, который выполняет эту функцию: https://packagist.org/packages/grasmash/yaml-expander

Пример 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} 

Пример логика:

// 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 (
    '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

Любить концепцию экспандера! –

2

YML определение:

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

Где-то в thymeleaf

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

Выход: /главная/данные/в/ /главная/данные/в/p1

+0

@AndrewBullock Я думаю, что это должен быть принятый ответ, так как он точно решает вашу проблему. –

+1

Нет, это не естественное использование переменной в YAML, и она не указана ни в каких версиях спецификации. После некоторого теста это не сработает. –

+0

Это, вероятно, работало для Pavol, используя что-то, что предварительно обработало yaml (т. Е. Maven-resources-plugin filtering) – DeezCashews

0

В некоторых языках, вы можете использовать библиотеку, например, tampax является реализация 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." 
Смежные вопросы