如果有 '!',如何使用 PyYAML 解析 YAML在 YAML 中
How to Parse YAML Using PyYAML if there are '!' within the YAML
我有一个 YAML 文件,我只想解析 description
变量;但是,我知道我的 CloudFormation 模板(YAML 文件)中的感叹号给 PyYAML 带来了麻烦。
我收到以下错误:
yaml.constructor.ConstructorError: could not determine a constructor for the tag '!Equals'
文件有多个!Ref
和!Equals
。我怎样才能忽略这些构造函数并获得我正在寻找的特定变量——在这种情况下,description
变量。
您可以使用自定义 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'])}}
如果你必须处理一个带有多个不同标签的 YAML 文档,并且
只对其中的一部分感兴趣,你仍然应该
处理所有这些。如果您感兴趣的元素是嵌套的
在其他标记结构中,您至少需要处理所有 "enclosing" 标记
正确。
但是没有必要单独处理所有标签,您
可以编写可以处理映射、序列的构造函数例程
和标量注册到 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 中没有变量,所以有点猜测是什么
你的意思是 "like to parse the description variable only"。如果那有
显式标记(例如 !Description
),您可以通过添加 2-3 行来过滤掉值
通过匹配 tag_suffix
参数到 any_constructor
。
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)
我有一个 YAML 文件,我只想解析 description
变量;但是,我知道我的 CloudFormation 模板(YAML 文件)中的感叹号给 PyYAML 带来了麻烦。
我收到以下错误:
yaml.constructor.ConstructorError: could not determine a constructor for the tag '!Equals'
文件有多个!Ref
和!Equals
。我怎样才能忽略这些构造函数并获得我正在寻找的特定变量——在这种情况下,description
变量。
您可以使用自定义 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'])}}
如果你必须处理一个带有多个不同标签的 YAML 文档,并且 只对其中的一部分感兴趣,你仍然应该 处理所有这些。如果您感兴趣的元素是嵌套的 在其他标记结构中,您至少需要处理所有 "enclosing" 标记 正确。
但是没有必要单独处理所有标签,您
可以编写可以处理映射、序列的构造函数例程
和标量注册到 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 中没有变量,所以有点猜测是什么
你的意思是 "like to parse the description variable only"。如果那有
显式标记(例如 !Description
),您可以通过添加 2-3 行来过滤掉值
通过匹配 tag_suffix
参数到 any_constructor
。
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)