2010-01-14 26 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."