如何从同一YAML文件中的其他地方引用YAML“设置”?
我有以下的YAML:
paths: patha: /path/to/root/a pathb: /path/to/root/b pathc: /path/to/root/c
我怎么能“正常化”这一点,通过从三个path中删除/path/to/root/
,并将其作为自己的设置,如:
paths: root: /path/to/root/ patha: *root* + a pathb: *root* + b pathc: *root* + c
很明显,这是无效的,我只是做了。 什么是真正的语法? 可以这样做吗?
我不认为这是可能的。 您可以重复使用“节点”,但不是其中的一部分。
bill-to: &id001 given : Chris family : Dumars ship-to: *id001
这是非常有效的YAML, given
字段和family
在ship-to
块中被重复使用。 你可以用同样的方法重新使用标量节点,但是没有办法改变里面的内容,并从YAML里面添加一个path的最后部分。
如果重复打扰你,我build议让你的应用程序知道root
属性,并将其添加到每个看起来相对不绝对的path。
是的,使用自定义标签。 Python中的示例,使!join
标记连接数组中的string:
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
的参数数组可以有任意数量的任何数据types的元素,只要它们可以转换为string,那么!join [*a, "/", *b, "/", *c]
会你会期待什么。
另一种看待这个问题的方法是简单地使用另一个字段。
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
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 / data / in / / home / data / in / p1
你的例子是无效的, 只是因为你select了一个保留字符来启动你的标量。 如果用其他非保留字符replace*
(我倾向于使用非ASCII字符,因为它们很less用作某个规范的一部分),那么最终会得到完全合法的YAML:
paths: root: /path/to/root/ patha: ♦root♦ + a pathb: ♦root♦ + b pathc: ♦root♦ + c
这将加载到分析器使用的语言中的映射的标准表示forms,不会神奇地扩展任何东西。
要做到这一点,请使用下面的Python程序中的本地默认对象types:
# 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
扩展是即时完成的,并处理嵌套的定义,但是你必须小心不要调用无限recursion。
通过指定转储器,您可以从加载的数据中转储原始的YAML,因为可以实时扩展:
dumper = yaml.RoundTripDumper dumper.add_representer(Paths, Paths.__yaml_out__) print(yaml.dump(paths, Dumper=dumper, allow_unicode=True))
这将改变映射键sorting。 如果这是一个问题,你必须使self.d
一个CommentedMap
(从ruamel.yaml.comments.py
导入)
我已经在Packagist上创build了一个可执行此function的库: 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', );
在某些语言中,可以使用替代库,例如, tampax是YAML处理variables的一个实现:
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."