我有以下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 :(得分:100)
我认为不可能。您可以重用“节点”但不能重用它。
bill-to: &id001
given : Chris
family : Dumars
ship-to: *id001
这是完全有效的YAML,字段given
和family
会在ship-to
块中重复使用。您可以以相同的方式重用标量节点,但是您无法更改内部的内容并从YAML内部添加路径的最后一部分。
如果重复打扰你那么多我建议让你的应用程序知道root
属性并将其添加到看起来相对而非绝对的每条路径。
答案 1 :(得分:55)
是的,使用自定义标签。 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]
可以达到预期效果。
答案 2 :(得分:14)
另一种看待这种情况的方法是简单地使用另一个字段。
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
答案 3 :(得分:4)
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 / p1
答案 4 :(得分:1)
我在Packagist上创建了一个执行此功能的库: https://packagist.org/packages/grasmash/yaml-expander
示例YAML文件:
// 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',
);
结果数组:
$("a").each(function(k, j) {
console.log(j.getAttribute("href"));
});
答案 5 :(得分:1)
在某些语言中,您可以使用替代库,例如,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."
答案 6 :(得分:1)
使用Yglu,您可以将示例编写为:
paths:
root: /path/to/root/
patha: !? .paths.root + a
pathb: !? .paths.root + b
pathc: !? .paths.root + c
免责声明:我是Yglu的作者。
答案 7 :(得分:0)
您的示例无效仅 ,因为您选择了保留字符来启动标量。如果将*
替换为其他非保留字符(我倾向于使用非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
导入)
答案 8 :(得分:0)
我已经在Python上编写了自己的库,以扩展从具有类似层次结构的目录中加载的变量:
/root
|
+- /proj1
|
+- config.yaml
|
+- /proj2
|
+- config.yaml
|
... and so on ...
此处的主要区别在于,仅在加载所有config.yaml
文件之后才能应用扩展,下一个文件中的变量可以覆盖前一个文件中的变量,因此伪代码应如下所示:
env = YamlEnv()
env.load('/root/proj1/config.yaml')
env.load('/root/proj1/proj2/config.yaml')
...
env.expand()
作为附加选项,xonsh
脚本可以将结果变量导出到环境变量中(请参见yaml_update_global_vars
函数)。
脚本:
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
专家:
${MYUNDEFINEDVAR}
-> *$/{MYUNDEFINEDVAR}
)${env:MYVAR}
)扩展引用\\
)中的所有/
替换为${env:MYVAR:path}
缺点:
${MYSCOPE.MYVAR}
之类的东西)