YAML错误:无法确定标记的构造函数

问题描述:

这与questions/44786412非常相似,但我的似乎是由YAML safe_load()触发的。我正在使用Ruamel的libraryYamlReader将一堆CloudFormation碎片粘合到一个合并的模板中。爆炸符号只是不正确的YAML?YAML错误:无法确定标记的构造函数

Outputs: 
    Vpc: 
    Value: !Ref vpc 
    Export: 
     Name: !Sub "${AWS::StackName}-Vpc" 

这些

Outputs: 
    Vpc: 
    Value: 
     Ref: vpc 
    Export: 
     Name: 
     Fn::Sub: "${AWS::StackName}-Vpc" 

Resources: 
    vpc: 
    Type: AWS::EC2::VPC 
    Properties: 
     CidrBlock: 
     Fn::FindInMap: [ CidrBlock, !Ref "AWS::Region", Vpc ] 

2部分没有问题;如何让load()单独留下'Fn :: Select:'的权利。

FromPort: 
    Fn::Select: [ 0, Fn::FindInMap: [ Service, https, Ports ] ] 

得到转换为此,现在CF不喜欢。

FromPort: 
    Fn::Select: [0, {Fn::FindInMap: [Service, https, Ports]}] 

如果我完全展开语句,那么没有问题。我想速记只是有问题。

FromPort: 
    Fn::Select: 
    - 0 
    - Fn::FindInMap: [Service, ssh, Ports] 
+0

你应该回退并删除“第2部分”。在其他问题上添加标签,甚至是相关的,并非如此。你可以将它作为一个新问题提出,但现在还不清楚:您提到的七个冒号中的哪一个?也“被转换为低于”,低于什么?在冒号下面? – Anthon

你 “砰符号” 是正确的YAML,通常这被称为标签。如果您想要使用safe_load()以及那些必须提供!Ref!Sub标记的构造函数,例如using:

ruamel.yaml.add_constructor(u'!Ref', your_ref_constructor, constructor=ruamel.yaml.SafeConstructor) 

其中对于这两个标签,您应该期望处理标量值。而不是更常见的映射。

我建议您使用RoundTripLoader而不是SafeLoader,这将保留订单,评论等。 RoundTripLoaderSafeLoader的子类。

如果您正在使用ruamel.yaml> = 0.15.33,支持往返标量,你可以做(​​使用新的ruamel.yaml API):

import sys 
from ruamel.yaml import YAML 

yaml = YAML() 
yaml.preserve_quotes = True 

data = yaml.load("""\ 
Outputs: 
    Vpc: 
    Value: !Ref: vpc # first tag 
    Export: 
     Name: !Sub "${AWS::StackName}-Vpc" # second tag 
""") 

yaml.dump(data, sys.stdout) 

获得:

Outputs: 
    Vpc: 
    Value: !Ref: vpc # first tag 
    Export: 
     Name: !Sub "${AWS::StackName}-Vpc" # second tag 

在较早的0.15.X版本中,您必须自己指定标量对象的类。这很麻烦,如果你有很多对象,但允许额外的功能:

import sys 
from ruamel.yaml import YAML 


class Ref: 
    yaml_tag = u'!Ref:' 

    def __init__(self, value, style=None): 
     self.value = value 
     self.style = style 

    @classmethod 
    def to_yaml(cls, representer, node): 
     return representer.represent_scalar(cls.yaml_tag, 
              u'{.value}'.format(node), node.style) 

    @classmethod 
    def from_yaml(cls, constructor, node): 
     return cls(node.value, node.style) 

    def __iadd__(self, v): 
     self.value += str(v) 
     return self 

class Sub: 
    yaml_tag = u'!Sub' 
    def __init__(self, value, style=None): 
     self.value = value 
     self.style = style 

    @classmethod 
    def to_yaml(cls, representer, node): 
     return representer.represent_scalar(cls.yaml_tag, 
              u'{.value}'.format(node), node.style) 

    @classmethod 
    def from_yaml(cls, constructor, node): 
     return cls(node.value, node.style) 


yaml = YAML(typ='rt') 
yaml.register_class(Ref) 
yaml.register_class(Sub) 

data = yaml.load("""\ 
Outputs: 
    Vpc: 
    Value: !Ref: vpc # first tag 
    Export: 
     Name: !Sub "${AWS::StackName}-Vpc" # second tag 
""") 

data['Outputs']['Vpc']['Value'] += '123' 

yaml.dump(data, sys.stdout) 

这给:

Outputs: 
    Vpc: 
    Value: !Ref: vpc123 # first tag 
    Export: 
     Name: !Sub "${AWS::StackName}-Vpc" # second tag 
+0

谢谢。现在我已经得到了更多,我已经删除了爆炸符号并切换到使用RoundTrip。但除了定义这些类(对定义CloudFormation的所有内部函数并不特别感兴趣),Yaml是否还有一种方法可以让单独的冒号留在任何地方,而不是解析它? –

+0

@MatthewPatton不,解析需要完成,要知道标签的结束位置,与之相关的值是什么等等。我更新了未定义的标记对象的catch-all以支持标量(它仅支持基于映射的对象),因此您正在使用并更新相应的答案。 – Anthon