我有以下YAML:

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

我如何“正常化”它,通过从三个路径中删除/path/到/root/,并将其作为自己的设置,类似于:

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

显然这是无效的,我瞎编的。真正的语法是什么?这能做到吗?


当前回答

YML定义:

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

在百里叶的某个地方

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

输出: / home /数据/ in / / home /数据/ in / p1

其他回答

我认为这是不可能的。您可以重用“节点”,但不能重用它的一部分。

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

这是完全有效的YAML,并且给出的字段和家族在ship-to block中被重用。您可以以同样的方式重用标量节点,但您无法更改内部内容并从YAML中添加路径的最后一部分。

如果重复让你很困扰,我建议让你的应用程序意识到根属性,并将它添加到每个看起来相对而不是绝对的路径。

是的,使用自定义标签。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]就是您所期望的那样。

在某些语言中,你可以使用替代库,例如,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."

编者注:海报作者也是本包的作者。

另一种方法是简单地使用另一个字段。

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

您的示例无效只是因为您选择了一个保留字符作为标量的开头。如果你用其他非保留字符替换*(我倾向于使用非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))

这将改变映射键的顺序。如果你有这个问题的话 成就自我。d一个commenttedmap(从ruame .yaml.comments.py导入)