【问题标题】:Load YAML without expanding tags?在不扩展标签的情况下加载 YAML?
【发布时间】:2019-09-28 20:06:48
【问题描述】:

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

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

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

【问题讨论】:

  • 好的,假设你得到了{"Foo"=>"!Bar baz"}。你用它做点什么,然后呢?您想将其转换回 YAML 吗? {"Foo" => "!Bar baz"}.to_yaml 将导致 Foo: "!Bar baz" - 这不是你开始使用的(它现在是一个字符串,不评估标签)。
  • 为什么不用File.read 加载它?
  • @JackTheRandom 影响 YAML 标量的输出方式看起来非常简单,您可以覆盖 ruby-doc.org/stdlib-2.0.0/libdoc/psych/rdoc/Psych/Emitter.html 中的相关方法。尽管如此,这仍然是一个好点:做类似github.com/burtcorp/regentanz/blob/… 的事情可能需要维护,但是理解和维护是微不足道的,而重新构造输出可能不是微不足道的,特别是如果baz 是一个映射或序列。我会接受这是正确的答案。
  • @Bittrance 好吧,我可以将其发布为答案。 :-)

标签: ruby yaml amazon-cloudformation


【解决方案1】:

你不应该需要创建每一个类型,你 需要做的是制作一个通用的标签处理例程,它着眼于 标签所在节点的类型(映射、序列、标量),然后 创建一个可以附加标签的 Ruby 类型的节点。

我不知道如何处理 PsychRuby,但你指出 两者都不是严格要求,大部分的辛苦 对于这种往返ruamel.yaml 对于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]

请注意:

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

  • 像锚点、合并、cmets、引号之类的东西都被保留了 特殊形式的整数(十六进制、八进制等)和浮点数。

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

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

【讨论】:

  • 啊,对不起!我的意思是 Ruby 2.0 不是一个严格的要求。代码需要进入github.com/burtcorp/cuffsert,所以它需要是Ruby。我真的很感谢你的努力。
【解决方案2】:

好的,假设你在解析 YAML 后得到了{"Foo"=&gt;"!Bar baz"}

你用它做了什么,然后你想把它转换回 YAML?

{"Foo" =&gt; "!Bar baz"}.to_yaml 将导致 Foo: "!Bar baz" - 这不是您开始使用的(现在是字符串,不评估标签)。

采用解析 YAML 的方式并非易事,也许应该做其他事情。

【讨论】:

猜你喜欢
  • 2023-04-08
  • 2016-04-03
  • 2016-02-06
  • 1970-01-01
  • 2015-03-07
  • 2017-07-09
  • 2017-05-20
  • 1970-01-01
  • 2011-11-29
相关资源
最近更新 更多