首页 > 解决方案 > 在不扩展标签的情况下加载 YAML?

问题描述

我正在加载 YAML 文件(特别是 CloudFormation 模板),其中可能包含!Ref我想将其视为普通字符串的自定义标签(例如 ),即YAML.safe_load('Foo: !Bar baz')会导致{"Foo"=>"!Bar baz"}或类似的结果。这是因为我想在将模板转储之前遍历和操作模板。我不希望https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.htmladd_tag下的所有内容。我目前正在使用 Psych 和 Ruby 2.0,但两者都不是严格要求。

更新 1:我的意思是说基于 Ruby 版本高于 2.0 的答案很好。

更新 2:我在这种情况下添加了 CloudFormation 标记,因为注册一堆!X->Fn::X转换可能是最不坏的解决方案,此时我不需要一般的 Ruby 问题。

标签: rubyyamlamazon-cloudformation

解决方案


您不需要创建每种类型,您需要做的是创建一个通用标签处理例程,查看标签所在节点的类型(映射、序列、标量),然后创建这样一个节点作为可以附加标签的 Ruby 类型。

我不知道如何使用Psychand来做到这一点Ruby,但是您指出这两者都不是严格的要求,而且这种往返的大部分艰苦工作都是ruamel.yamlfor Python (免责声明:我是该软件包的作者)。

如果这是您的输入文件input.yaml

Foo: !Bar baz
N1:
   - !mytaggedmaptype
     parm1: 3
     parm3: 4
   - !mytaggedseqtype
     - 8
     - 9
N2: &someanchor1
   a: "some stuff"
   b: 0.2e+1
   f: |
     within a literal scalar newlines
     are preserved
N3: &someanchor2
   c: 0x3
   b: 4    # this value is not taken, as the first entry found is taken 
   ['the', 'answer']: still unknown
   {version: 28}: tested!
N4:
   d: 5.000
   <<: [*someanchor1, *someanchor2]

然后这个 Python (3) 程序:

import sys
from pathlib import Path
import ruamel.yaml

yaml_in = Path('input.yaml')
yaml_out = Path('output.yaml')


yaml = ruamel.yaml.YAML()
yaml.preserve_quotes = True
# uncomment next line if your YAML is the outdated version 1.1 YAML but has no tag
# yaml.version = (1, 1) 
data = yaml.load(yaml_in)

# do your updating here
data['Foo'].value = 'hello world!'  # see the first of the notes
data['N1'][0]['parm3'] = 4444
data['N1'][0].insert(1, 'parm2', 222)
data['N1'][1][1] = 9999
data['N3'][('the', 'answer')] = 42

# and dump to file
yaml.dump(data, yaml_out)

创建output.yaml

Foo: !Bar hello world!
N1:
- !mytaggedmaptype
  parm1: 3
  parm2: 222
  parm3: 4444
- !mytaggedseqtype
  - 8
  - 9999
N2: &someanchor1
  a: "some stuff"
  b: 0.2e+1
  f: |
    within a literal scalar newlines
    are preserved
N3: &someanchor2
  c: 0x3
  b: 4     # this value is not taken, as the first entry found is taken 
  ['the', 'answer']: 42
  {version: 28}: tested!
N4:
  d: 5.000
  <<: [*someanchor1, *someanchor2]

请注意:

  • 您可以在保留标量上的标记的同时更新标记的标量,但是由于您将这样的标量替换为它的赋值(而不是像列表(序列/数组)或字典(映射/哈希)那样更新值,您不能只分配新值,否则您将丢失标记信息,您必须更新.value属性。

  • 诸如锚点、合并、注释、引号之类的东西被保留,特殊形式的整数(十六进制、八进制等)和浮点数也是如此。

  • 对于作为映射键的 YAML 序列,您需要使用元组 ( ('the', 'answer')) 而不是序列 ( ['the', 'answer']),因为 Python 不允许在映射中使用可变键。对于作为映射键的 YAM 映射,您需要使用不可变 Mapping的 from collections.abc。(我不确定 Psych 是否支持这些有效的 YAML 密钥)

  • 如果您需要更新锚定/别名标量,请参阅此内容


推荐阅读