如何在YAML中引用别名映射值

时间:2019-01-19 22:20:28

标签: yaml

我感觉这是不可能的,但是我有一个YAML片段,如下所示:

.map_values: &my_map
  a: 'D'
  b: 'E'
  a: 'F'

section:
  stage: *my_map['b']

我希望stage的值为E

在YAML中这可能吗?我已经尝试过几乎所有我能想到的替代品。

1 个答案:

答案 0 :(得分:0)

由于映射中存在重复的键,因此不允许 在YAML 1.2中(至少应在YAML 1.1中引发警告),这是 不起作用,但是即使您纠正了该错误,也无法做到这一点 只是锚和别名。

YAML中唯一可用的替代之类的替代方法是"Merge Key Language-Independent Type"。这在YAML规范中间接引用,但并未包含在其中,但在大多数解析器中都可用。

唯一允许它执行的操作是使用一个或多个其他映射的键值对“更新”映射,如果该映射中尚不存在该键。为此,您可以使用特殊键<<,该特殊键具有别名或别名列表。

YAML规范中没有指定指定的功能来取消引用特殊密钥。

有些系统使用的模板会生成YAML,但是在此处应用这些模板存在两个主要问题:

  • 模板语言本身经常与YAML语法中的指示符冲突, 将模板设为无效的YAML

  • ,即使可以将模板作为有效的YAML加载,并且提取了 更新模板的其他部分,您需要将输入解析两次(一次获取 值以更新模板,然后解析更新的模板)。有潜力 YAML的复杂性及其解析器的相对较慢的速度,这可能令人望而却步

您可以做的是创建一些标签(例如!lookup),并让其构造函数解释该节点。 由于该节点必须再次是有效的YAML,因此您必须决定使用序列还是映射。 在这两种情况下,您都必须为值和键包含一些特殊的语法 (例如在合并中使用的<<)。

在示例中,我省略了虚假的单引号,具体取决于 您真正的价值观,您当然会需要它们。

使用序列的示例:

.map_values: &my_map
  a: D
  b: E
  c: F

section: !Lookup
- *my_map
- stage: <b>

使用映射的示例:

.map_values: &my_map
  a: D
  b: E
  c: F

section: !Lookup
  >>: *my_map
  stage: <b>

都可以即时构建数据(即没有过去的数据) 加载处理您的数据结构所必需的)。例如。使用Python和 input.yaml中的序列“样式”:

import sys
import ruamel.yaml
from pathlib import Path

input = Path('input.yaml')

yaml = ruamel.yaml.YAML(typ='safe')
yaml.default_flow_style = False

@yaml.register_class
class Lookup:
    @classmethod
    def from_yaml(cls, constructor, node):
         """
            this expects a two entry sequence, in which the first is a mapping X, typically using
            an alias
            the second entry should be an mapping, for which the values which have the form <key>
            are looked up in X
            non-existing keys will throw an error during loading.
         """
         X, res = constructor.construct_sequence(node, deep=True)
         yield res
         for key, value in res.items():
             try:
                 if value.startswith('<') and value.endswith('>'):
                   res[key] = X[value[1:-1]]
             except AttributeError:
                 pass
         return res


data = yaml.load(input)
yaml.dump(data, sys.stdout)

给出:

.map_values:
  a: D
  b: E
  c: F
section:
  stage: E

有几件事要注意:

  • 使用<...>是任意的,您既不需要开头也不需要 结束标记。我建议您使用一些没有 在YAML中具有特殊含义,因此您无需引用值。您可以例如用一些 众所周知的unicode点,但是在编辑器中键入它们往往很麻烦。
  • 调用from_yaml时,锚点尚未完全构建。所以X是一个空字典 稍后会被填充。用yield构造的过程执行两步过程:我们首先 将res“按原样”返回给构造函数,然后稍后对其进行更新。的构造阶段 加载程序知道如何在获得生成器而不是“正常”值时自动进行处理。
  • try .. except可以处理不是字符串的映射值(即数字,日期,布尔值)。
  • 您也可以在键中进行替换,只需确保删除旧键即可。

由于代码是标准的YAML,因此上述代码应该可以以任何一种方式实现 YAML解析器,与语言无关。