【问题标题】:How to print out the ! in pyaml?怎么打印出来!在pyaml中?
【发布时间】:2017-04-17 20:38:02
【问题描述】:

我有代码可以将字典打印为 YAML:

    import yaml
    yaml.dump(
        {
        "Properties":
             {
                 "ImageId": "!Ref AParameter"
             }
        },
        new_template,
        default_flow_style=False
    )

这会创建:

Properties:
  ImageId: '!Ref AParameter'

注意 ImageId 的值是如何在引号内的?我想不带引号打印。我如何使用 PyYAML 做到这一点?

【问题讨论】:

  • ! 在 YAML 中有特殊的含义。打印不带引号的值会改变 YAML 的含义。
  • 也许吧。但是,我确实希望它是那样的。
  • @doles 如果您解释是什么让您“喜欢”不带引号的输出,那将会很有帮助。您是否了解加载您生成的输出的程序的含义并不完全清楚。我为您的确切输出提供了一个解决方案,并提供了加载输出所需的代码。
  • 当然。我正在尝试以编程方式创建 AWS cloudformation 模板 (docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/…) 。在 AWS cloudformation 模板中,一个值可以是对模板中其他位置的另一个值的引用。这样的参考是通过使用我的问题中的语法进行编码的。因此,我希望能够打印出我所问的内容。我可以尝试其他形式,但是,这是规定的格式。
  • @doles 我没有找到!Ref 的示例(但后来我没有浏览所有页面)。如果 AWS 假设您手动编写 YAML 文件,那么包含 !Ref 当然是微不足道的,但是根据我的回答,从数据结构中转储它更加复杂。尽管使用符合标准的 YAML 解析器 !Ref "AParameter" 应该始终加载与 !Ref AParameter 相同的内容。

标签: python python-2.7 yaml pyyaml


【解决方案1】:

! 具有特殊含义,因为它用于引入an explicit tag,因此不能出现在普通(不带引号)样式标量的开头。具体来说,YAML 1.2 规范的rule 126 表明,这种普通标量的第一个字符不能是c-indicator! 就是这样。

这样的标量必须被引用(单引号或双引号),PyYAML 会自动执行此操作,或者以文字或折叠块样式放置。

您可以将不带引号的有效 YAML 转储到文字块样式标量:

Properties:
  ImageId: |
    !Ref AParameter

没有支持性的编程 PyYAML 无法做到这一点。您可以使用 ruamel.yaml 来执行此操作(免责声明:我是该软件包的作者),方法是将值设为 PreservedScalarString 实例:ruamel.yaml.scalarstring.PreservedScalarString("!Ref AParameter")

您当然可以使用!Ref 标记定义转储类,但标记上下文将强制标量AParameter 周围加引号:

import sys
import yaml

class Ref(str):
    @staticmethod
    def yaml_dumper(dumper, data):
        return dumper.represent_scalar('!Ref', u'{}'.format(data), style=None)

yaml.add_representer(Ref, Ref.yaml_dumper)


yaml.dump(
    {
        "Properties":
        {
            "ImageId": Ref("AParameter"),
        }
    },
    sys.stdout,
    default_flow_style=False,
)

给出:

Properties:
  ImageId: !Ref 'AParameter'

尽管使用适当的构造函数加载!Ref Aparameter 是可能的(即,为了安全起见,这里只是添加了引号)。

如果您还想取消这些引号,您可以例如使用 ruamel.yaml 执行此操作,为您的节点定义一个特殊样式 'x' 并为此提供发射处理:

from ruamel import yaml

class Ref(str):
    @staticmethod
    def yaml_dumper(dumper, data):
        return dumper.represent_scalar('!Ref', u'{}'.format(data), style='x')

    @staticmethod
    def yaml_constructor(loader, node):
        value = loader.construct_scalar(node)
        return Ref(value)

yaml.add_representer(Ref, Ref.yaml_dumper)
yaml.add_constructor('!Ref', Ref.yaml_constructor,
                     constructor=yaml.constructor.SafeConstructor)

def choose_scalar_style(self):
    if self.event.style == 'x':
        return ''
    return self.org_choose_scalar_style()

yaml.emitter.Emitter.org_choose_scalar_style = yaml.emitter.Emitter.choose_scalar_style
yaml.emitter.Emitter.choose_scalar_style = choose_scalar_style

data =  {
    "Properties":
    {
        "ImageId": Ref("AParameter"),
    }
}

ys = yaml.dump(data, default_flow_style=False)

print(ys)
data_out = yaml.safe_load(ys)
assert data_out == data

上述内容不会在断言上引发错误,因此数据往返和打印输出正是您想要的 AFAICT:

Properties:
  ImageId: !Ref AParameter

【讨论】:

  • 哇!感谢您的详细回复。我很快就会试试这个。
  • 我试过了,它确实能打印出我想要的东西。由于TypeError: unbound method yaml_constructor() must be called with Ref instance as first argument (got SafeLoader instance instead),倒数第二行失败。因此,断言永远不会被评估。但是,它有效。
  • @doles yaml_constructor() 必须装饰@static_method,就像yaml_dumper(),我更新了答案。很抱歉。
猜你喜欢
  • 2012-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-04
  • 1970-01-01
  • 1970-01-01
  • 2016-12-29
  • 1970-01-01
相关资源
最近更新 更多