2010-01-14 112 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 2017-10-02 19:42:51

回答

64

我不認爲這是可能的。您可以重複使用「節點」,但不是其中的一部分。

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

這是完全有效的YAML和字段givenfamily被重用在ship-to塊。您可以重複標節點的方式相同,但有沒有辦法可以改變裏面的內容,並從裏面YAML添加到它的路徑是最後一部分。

如果重複打擾你這麼多,我建議,讓您的應用感知root財產,並把它添加到每一個看起來相對而不是絕對路徑。

+1

好的,謝謝,是的,生病了,必須在代碼中加上'root'。沒什麼大不了的。 – 2010-01-14 11:40:27

+2

接受的答案不準確。查看我的答案以獲得解決方案。 – 2014-07-10 10:57:03

+0

如何做到這一點,如果** bill-to **在另一個文件中,我們已經導入了** ship-to **被定義的文件。 – 2015-02-04 05:56:47

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 2015-06-06 07:07:28

+0

誰投下了這個票 - 你能說爲什麼要這麼做嗎? – 2016-03-31 11:21:13

+0

向下箭頭的亮點是「這個答案沒有用處」,對於任何對YAML主題不感興趣的人來說都是如此。更糟糕的是,除了我之外,沒有人會因爲我是目前唯一的評論者而收到您的評論/問題的通知。沮喪的人回到這裏並讀出你的評論和答案的機會太低,我會認爲那些接近不存在的人。話雖如此,那不是我,如果我是你,我不會要求並刪除評論。 – Anthon 2016-03-31 11:34:10

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.d一個CommentedMap(從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

愛的膨脹機概念! – 2018-01-09 15:12:33

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我認爲這應該是公認的答案,因爲它完全解決了你的問題。 – 2017-10-02 13:30:08

+1

不,它不是YAML中變量的本地用法,它在任何規範版本中都沒有指定。經過一些測試,這是行不通的。 – 2017-10-30 20:30:06

+0

這可能對Pavol使用預處理yaml(即maven-resources-plugin過濾) – DeezCashews 2017-11-17 14:01:07

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