aynı YAML dosyasındaki başka bir yerden bir YAML “ayarına” nasıl başvurulur?


146

Aşağıdaki YAML var:

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

Bunu /path/to/root/üç yoldan kaldırarak ve " kendi ayarı olarak" gibi bir şeyle nasıl "normalleştirebilirim" :

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

Açıkçası bu geçersiz, ben uydurdum. Gerçek sözdizimi nedir? Bu yapılabilir mi?


Yanıtlar:


127

Bunun mümkün olduğunu düşünmüyorum. "Düğümü" tekrar kullanabilirsiniz, ancak bir kısmını kullanamazsınız.

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

Bu tamamen geçerli YAML ve alanlar givenolup blok familyolarak yeniden kullanılır ship-to. Skaler düğümü aynı şekilde tekrar kullanabilirsiniz, ancak içindekileri değiştirmenin ve bir yolun son kısmını YAML içinden eklemenin bir yolu yoktur.

Tekrar sizi rahatsız ediyorsa, uygulamanızı rootözellikten haberdar etmenizi ve mutlak değil göreceli görünen her yola eklemenizi öneririm .


1
Tamam teşekkürler, evet rootkodu girmeliyim . biggie yok.
Andrew Bullock

2
Kabul edilen cevap doğru değil. Çözüm için cevabımı görün.
Chris Johnson

eğer nasıl Bunu yapmak için fatura için nerede ithal hangi başka bir dosyada olduğu gemi için tanımlanır?
Prateek Jain

@PrateekJain: Birden fazla dosyayla uğraşıyorsanız, muhtemelen burada listelenen gibi bağımsız bir YAML geliştirme kitaplığını değerlendirmek için en iyisini yaparsınız. github.com/dreftymac/dynamic.yaml/blob/master/…
dreftymac

1
Yaml.org/spec/1.2/spec.html'deki örnek 2.9'a bakın ; biri de harika skaler referans olabilir
akostadinov

72

Evet, özel etiketler kullanarak. !joinEtiketin bir dizideki birleştirme dizelerini oluşturmasına örnek olarak :

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

Sonuç:

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

Argüman dizisi, !joindizeye dönüştürülebildiği sürece herhangi bir veri türünde herhangi bir sayıda öğeye sahip olabilir, !join [*a, "/", *b, "/", *c]beklediğiniz gibi.


2
Ben çözüm daha hoş, biraz daha az okunabilir YAML pahasına benim kodlama sonra daha basit.
Anthon

7
Bu cevap daha fazla oyu hak ediyor. Teknik olarak YAML şartnamesine göre en doğru cevaptır. Bununla birlikte, gerçek YAML uygulamaları uyarınca bir uyarı vardır, aslında tam YAML spesifikasyonunu uygulayan çok az şey vardır. Python'un piramitleri, spesifikasyon ile homojenliği açısından diğerlerinin üstünde ve ötesindedir.
dreftymac

5
Soru, bir yaml dosyasındaki bir değere başvurmakla ilgili gibi görünüyor. Etrafına başka bir kod katmanı eklemek benim tercih ettiğim çözüm olmayacaktı.
user2020056 15:17

1
@ChrisJohnson Bu cevap için teşekkürler, bu sözdizimini listeleyen bir referans belgeniz olup olmadığını merak ediyordum. YAML spec web üzerinde birden fazla yerde açıkladı gördüm, bu yüzden ben sadece aynı referans bakmak emin olmak istiyorum. Teşekkürler!
user5359531

3
Bu çözüm benim için ( python3?) İşe yaramadı, ancak yukarıda basit bir değişiklikle beklendiği gibi çalışıyor. Özellikle:yaml.SafeLoader.add_constructor(tag='!join', constructor=join) yaml.load(open(fpth, mode='r'), Loader=yaml.SafeLoader)
benjaminmgross

20

Buna bakmanın bir başka yolu da sadece başka bir alan kullanmaktır.

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

5

YML tanımı:

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

Kekik içinde bir yerde

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

Çıktı: / home / data / in / / home / data / in / p1


@AndrewBullock Sorununuzu tam olarak çözdüğü için bunun kabul edilen cevap olması gerektiğini düşünüyorum.
Honza Zidek

5
Hayır, YAML'de değişkenin yerel kullanımı değildir ve herhangi bir belirtim sürümünde belirtilmemiştir. Bazı testlerden sonra, bu işe yaramaz.
Arthur Lacoste

2
Bu muhtemelen Pavol için yaml'yi önceden işleyen bir şey kullanarak çalıştı (yani maven-resources-plugin filtering)
DeezCashews

1
Standart değil Yaml
Dan Niero

3

Packagist'te bulunan ve bu işlevi yerine getiren bir kütüphane oluşturdum: https://packagist.org/packages/grasmash/yaml-expander

Örnek YAML dosyası:

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}

Örnek mantık:

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

Sonuç dizisi:

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

Genişletici konseptini seviyorum!
Guillaume Roderick

2

Bazı dillerde, alternatif bir kitaplık kullanabilirsiniz. Örneğin, tampax YAML işleme değişkenlerinin bir uygulamasıdır:

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

1

Örneğinizin geçersiz olması, yalnızca skalerlerinizi başlatmak için ayrılmış bir karakter seçmiş olmanızdır. Eğer *diğer bazı ayrılmış olmayan karakterle değiştirirseniz (nadiren bazı spesifikasyonların bir parçası olarak kullanıldığı için ASCII olmayan karakterler kullanma eğilimindeyim), mükemmel yasal YAML ile sonuçlanırsınız:

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

Bu, ayrıştırıcınızın kullandığı dilde eşlemeler için standart temsili yükleyecek ve hiçbir şeyi sihirli bir şekilde genişletmeyecektir.
Bunu yapmak için aşağıdaki Python programında olduğu gibi yerel olarak varsayılan bir nesne türü kullanın:

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

yazdırılacak:

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

Genişletme anında yapılır ve iç içe tanımlarla ilgilenir, ancak sonsuz özyineleme başlatmama konusunda dikkatli olmalısınız.

Damperi belirterek, orijinal YAML'yi anında genişletme nedeniyle yüklü verilerden atabilirsiniz:

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

bu eşleme anahtarı sırasını değiştirecektir. Bu bir sorunsa self.d, CommentedMap(içe aktarılmış ruamel.yaml.comments.py) bir


0

Python dizinlerini yüklenen değişkenleri genişletmek için kendi kütüphanemi yazdım:

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

Buradaki temel fark, genişletmenin yalnızca tüm config.yamldosyalar yüklendikten sonra uygulanması gerektiğidir , burada bir sonraki dosyadaki değişkenler öncekinden değişkenleri geçersiz kılabilir, bu nedenle sözde kod şöyle görünmelidir:

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

Ek bir seçenek olarak xonshkomut dosyası, ortaya çıkan değişkenleri ortam değişkenlerine verebilir ( yaml_update_global_varsişleve bakın ).

Senaryolar:

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

Artıları :

  • basit, özyineleme ve iç içe değişkenleri desteklemez
  • tanımlanmamış bir değişkeni yer tutucuyla değiştirebilir ( ${MYUNDEFINEDVAR}-> *$/{MYUNDEFINEDVAR})
  • ortam değişkeninden ( ${env:MYVAR}) referansı genişletebilir
  • Bütün yerini alabilir \\için /(bir yol değişkeni ${env:MYVAR:path})

Eksileri :

  • yuvalanmış değişkenleri desteklemediğinden, yuvalanmış sözlüklerdeki değerleri genişletemez (benzeri ${MYSCOPE.MYVAR}bir şey uygulanmaz)
  • yer tutucu koyduktan sonra yineleme dahil genişletme özyinelemesini algılamaz

0

Yglu ile örneğinizi şöyle yazabilirsiniz:

paths:
  root: /path/to/root/
  patha: !? .paths.root + a
  pathb: !? .paths.root + b
  pathc: !? .paths.root + c

Feragatname: Yglu'nun yazarıyım.


Bu işlevselliği
YAML'ın
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.