15

我正在寻找一种工具或流程,它可以轻松获取包含锚点、别名和合并键的 YAML 文件,并扩展别名并合并到一个平面 YAML 文件中。仍然有许多不完全支持合并的常用 YAML 解析。

我希望能够利用合并来保持干燥,但在某些情况下,这需要构建到一个更详细的“平面”YAML 文件中,以便它可以被其他依赖于不完整的工具使用YAML 解析器。

示例源 YAML:

default: &DEFAULT
  URL: website.com
  mode: production  
  site_name: Website
  some_setting: h2i8yiuhef
  some_other_setting: 3600

development:
  <<: *DEFAULT
  URL: website.local
  mode: dev

test:
  <<: *DEFAULT
  URL: test.website.qa
  mode: test

所需的输出 YAML:

default:
  URL: website.com
  mode: production  
  site_name: Website
  some_setting: h2i8yiuhef
  some_other_setting: 3600

development:
  URL: website.local
  mode: dev
  site_name: Website
  some_setting: h2i8yiuhef
  some_other_setting: 3600

test:
  URL: test.website.qa
  mode: test
  site_name: Website
  some_setting: h2i8yiuhef
  some_other_setting: 3600
4

3 回答 3

11

如果您的系统上安装了 python,您可以执行pip install ruamel.yaml.cmd¹ 然后:

yaml merge-expand input.yaml output.yaml

(替换output.yaml-写入标准输出)。这实现了合并扩展,保留关键顺序和注释。

上面实际上是几行使用ruamel.yaml¹ 的代码,所以如果你有 Python(2.7 或 3.4+)并使用它安装pip install ruamel.yaml并将以下内容保存为expand.py

import sys
from ruamel.yaml import YAML

yaml = YAML(typ='safe')
yaml.default_flow_style=False
with open(sys.argv[1]) as fp:
    data = yaml.load(fp)
with open(sys.argv[2], 'w') as fp:
    yaml.dump(data, fp)

你已经可以做到:

python expand.py input.yaml output.yaml

这将为您提供在语义上与您请求的内容等效的 YAML(在output.yaml映射的键中已排序,在此程序输出中它们不是)。

以上假设您的 YAML 中没有任何标签,也不关心保留任何评论。其中大部分以及密钥顺序可以通过使用标准YAML()实例的修补版本来保留。修补是必要的,因为标准YAML()实例也保留了往返的合并,这正是您不想要的:

import sys
from ruamel.yaml import YAML, SafeConstructor

yaml = YAML()

yaml.Constructor.flatten_mapping = SafeConstructor.flatten_mapping
yaml.default_flow_style=False
yaml.allow_duplicate_keys = True
# comment out next line if you want "normal" anchors/aliases in your output
yaml.representer.ignore_aliases = lambda x: True  

with open(sys.argv[1]) as fp:
    data = yaml.load(fp)
with open(sys.argv[2], 'w') as fp:
    yaml.dump(data, fp)

使用此输入:

default: &DEFAULT
  URL: website.com
  mode: production
  site_name: Website
  some_setting: h2i8yiuhef
  some_other_setting: 3600  # an hour?

development:
  <<: *DEFAULT
  URL: website.local     # local web
  mode: dev

test:
  <<: *DEFAULT
  URL: test.website.qa
  mode: test

这将给出此输出(请注意,对合并键的注释会重复):

default:
  URL: website.com
  mode: production
  site_name: Website
  some_setting: h2i8yiuhef
  some_other_setting: 3600  # an hour?

development:
  URL: website.local     # local web
  mode: dev

  site_name: Website
  some_setting: h2i8yiuhef
  some_other_setting: 3600  # an hour?

test:
  URL: test.website.qa
  mode: test
  site_name: Website
  some_setting: h2i8yiuhef
  some_other_setting: 3600  # an hour?

以上是yaml merge-expand此答案开头提到的命令的作用。


¹免责声明:我是该软件包的作者。

于 2017-07-07T11:24:53.703 回答
2

更新:2019-03-13 12:41:05

  • 此答案已根据Anthon的评论进行了修改,该评论正确识别了 PyYAML 的限制。(见下文的陷阱)。

语境

  • YAML 文件
  • 用于解析 YAML 的 Python

问题

  • 用户 jtYamlEnthusiast 希望输出带有别名、锚点和合并键的 YAML 文件的非 DRY版本。

解决方案

  • 备选方案 1:使用ruamelAnthon infra 推广的库。
  • 备选方案 2:使用 Pythonpprint.pformat并简单地进行加载/转储往返转换。

基本原理

  • ruamel如果您可以自行决定安装除 pyyaml 之外的另一个 python 库,并且您希望高度控制“往返”YAML 转换(例如保留 YAML 注释),则该库非常棒。
  • 如果您不需要对往返 YAML 进行严格控制,或者由于其他原因受限于 pyyaml,您可以直接加载和转储 YAML,以获得“非 DRY”输出。

陷阱

  • 在撰写本文时,关于 YAML v1.1 和 YAML v1.2 的处理,PyYAML相对于库有限制ruamel

  • 也可以看看

例子

    ##
    import pprint
    import yaml
    ##
    myrawyaml = '''
    default: &DEFAULT
      URL: website.com
      mode: production
      site_name: Website
      some_setting: h2i8yiuhef
      some_other_setting: 3600

    development:
      <<: *DEFAULT
      URL: website.local
      mode: dev

    test:
      <<: *DEFAULT
      URL: test.website.qa
      mode: test
    '''
    ##
    pynative  =   yaml.safe_load(myrawyaml)
    vout      =   pprint.pformat(pynative)
    print(vout)                             ##=> this is non-DRY and just happens to be well-formed YAML syntax
    print(yaml.safe_load(vout))             ##=> this proves we have well-formed YAML if it loads without exception
于 2017-10-30T21:37:53.457 回答
0

如果您出于某种原因需要将扩展​​的 YAML 作为 YAML 写回文件,您可以:

  • 使用@Anthon 的答案。但是,如上所述,如果您无法安装软件包,这种方法可能不可行。

  • 使用@dreftymac 的答案。看来这个答案对某些人有用,但对我没有用;根据我的理解,pprint.pformat将参数作为其 Python 表示的字符串返回,并yaml.safe_load期望 Python 表示本身。当然,您可以使用eval由 返回的字符串pprint.pformat,但eval即使在受信任的输入上使用也会让人感到恶心。(同样,答案有几个赞成票,所以也许我在这里遗漏了一些东西。)

或者,你可以做我所做的:

import json
import yaml

def expand_yml(yml):
    return yaml.dump(json.loads(json.dumps(yml)))

expand_yml(my_yml_with_aliases)

由于 JSON 可以(除了一些例外,例如别名)被视为 YAML 的严格子集,因此这种方法通常应该有效。但是,如果性能是一个问题,或者如果您正在处理更复杂的 YAML,那么这种方法可能不适合您。

于 2020-11-24T19:29:05.380 回答