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

时间:2018-09-09 01:12:06

标签: python constructor yaml amazon-cloudformation pyyaml

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

我收到以下错误:

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

文件有许多!Ref!Equals。我该如何忽略这些构造函数,并获取要查找的特定变量-在这种情况下为description变量。

2 个答案:

答案 0 :(得分:3)

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

但是,您无需单独处理所有标签 可以编写一个构造函数例程来处理映射,序列 并且标量使用以下命令将其注册到PyYAML的SafeLoader

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)

给出:

{'MyEIP': {'Type': ['::', ['AWS', 'EC2', 'EIP']], 'Properties': {'InstanceId': 'MyEC2Instance'}}}

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

如果您的代码中出现意外的!Join标记,如上所示,该标记也将继续起作用, 以及任何其他标签,例如!Equal。标签刚刚被删除。

由于YAML中没有变量,因此有些猜测 您的意思是“只喜欢解析描述变量”。如果有 一个显式标记(例如!Description),您可以通过添加2-3行来过滤掉这些值 通过匹配any_constructor参数将其设置为tag_suffix

    if tag_suffix == u'!Description':
        description.append(loader.construct_scalar(node))

但是,映射中更可能有一些键为标量description, 并且您对与该键关联的值感兴趣。

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

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

from ruamel.yaml import YAML

with YAML() as yaml:
    data = yaml.load(inp)

答案 1 :(得分:1)

您可以使用自定义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)

输出:

{'Conditions': {'CreateNewSecurityGroup': Equals([Ref(ExistingSecurityGroup), 'NONE'])}}