如果有“!”,如何使用 PyYAML 解析 YAML 在 YAML 中

won*_*oup 3 python constructor yaml pyyaml aws-cloudformation

我有一个 YAML 文件,我只想解析description变量;但是,我知道我的 CloudFormation 模板(YAML 文件)中的感叹号给 PyYAML 带来了麻烦。

我收到以下错误:

yaml.constructor.ConstructorError: could not determine a constructor for the tag '!Equals'

该文件有很多!Ref!Equals. 我怎样才能忽略这些构造函数并获得我正在寻找的特定变量——在这种情况下,description变量。

Ant*_*hon 6

如果您必须处理具有多个不同标签的 YAML 文档,并且只对其中的一个子集感兴趣,您仍然应该处理它们。如果您感兴趣的元素嵌套在其他标记结构中,您至少需要正确处理所有“封闭”标记。

然而,无需单独处理所有标签,您可以编写一个构造函数例程,该例程可以处理映射、序列和标量,SafeLoader使用以下命令将其注册到 PyYAML :

import yaml

inp = """\
MyEIP:
  Type: !Join [ "::", [AWS, EC2, EIP] ]
  Properties:
    InstanceId: !Ref MyEC2Instance
"""

description = []

def any_constructor(loader, tag_suffix, node):
    if isinstance(node, yaml.MappingNode):
        return loader.construct_mapping(node)
    if isinstance(node, yaml.SequenceNode):
        return loader.construct_sequence(node)
    return loader.construct_scalar(node)

yaml.add_multi_constructor('', any_constructor, Loader=yaml.SafeLoader)

data = yaml.safe_load(inp)
print(data)
Run Code Online (Sandbox Code Playgroud)

这使:

{'MyEIP': {'Type': ['::', ['AWS', 'EC2', 'EIP']], 'Properties': {'InstanceId': 'MyEC2Instance'}}}
Run Code Online (Sandbox Code Playgroud)

inp也可以是打开的文件以供阅读)。

如您所见,如果!Join您的代码中出现意外标记以及任何其他标记(如!Equal. 标签只是被丢弃。

由于 YAML 中没有变量,因此“仅解析描述变量”是什么意思有点猜测。如果它具有显式标记(例如!Description),您可以any_constructor通过匹配tag_suffix参数向 中添加 2-3 行来过滤掉值。

    if tag_suffix == u'!Description':
        description.append(loader.construct_scalar(node))
Run Code Online (Sandbox Code Playgroud)

然而,映射中的某个键更有可能是 scalar description,并且您对与该键关联的值感兴趣。

    if isinstance(node, yaml.MappingNode):
        d = loader.construct_mapping(node)
        for k in d:
        if k == 'description':
            description.append(d[k])
        return d
Run Code Online (Sandbox Code Playgroud)

如果您知道数据层次结构中的确切位置,您当然也可以遍历data结构并根据键或列表位置提取您需要的任何内容。特别是在那种情况下,您最好使用 my ruamel.yaml,这是否可以在往返模式下加载标记的 YAML 而无需额外的努力(假设上述情况inp):

from ruamel.yaml import YAML

with YAML() as yaml:
    data = yaml.load(inp)
Run Code Online (Sandbox Code Playgroud)


Edd*_*onk 3

您可以使用自定义构造函数来定义自定义构造函数yaml.SafeLoader

import yaml

doc = '''
Conditions: 
  CreateNewSecurityGroup: !Equals [!Ref ExistingSecurityGroup, NONE]
'''

class Equals(object):
    def __init__(self, data):
        self.data = data
    def __repr__(self):
        return "Equals(%s)" % self.data

class Ref(object):
    def __init__(self, data):
        self.data = data
    def __repr__(self):
        return "Ref(%s)" % self.data

def create_equals(loader,node):
    value = loader.construct_sequence(node)
    return Equals(value)

def create_ref(loader,node):
    value = loader.construct_scalar(node)
    return Ref(value)

class Loader(yaml.SafeLoader):
    pass

yaml.add_constructor(u'!Equals', create_equals, Loader)
yaml.add_constructor(u'!Ref', create_ref, Loader)
a = yaml.load(doc, Loader)
print(a)
Run Code Online (Sandbox Code Playgroud)

输出:

{'Conditions': {'CreateNewSecurityGroup': Equals([Ref(ExistingSecurityGroup), 'NONE'])}}
Run Code Online (Sandbox Code Playgroud)