如何根据 python 键的值将 YAML 解析为多个 compose.yaml

How can I parse YAML into multiple compose.yaml based on the value of a key with python

我正在解析 YAML 并将其分解为多个不同的 YAML 文件。我是用PyYAML的constructor实现的,但是效果很差

这是我项目的一部分,我需要根据收到的 yaml 文件中的键值解析并拆分为多个不同的 yaml 文件。

我收到的 yaml 文件看起来像这样

testname: testname
testall:
    test1:
        name: name1
        location: 0
    test2: 
        name: name2
        location: 2
    test3: 
        name: name3
        location: 0
    test4: 
        name: name4
        location: 2
    ...
locations:
    - 0
    - 2
    - ...  

我想解析它并按设备拆分,如下所示:

# location0.yaml
testname:test
tests:
    test1:
        name:test1
        location:0
    test3: 
        name: test3
        location: 0
# location2.yaml
testname:test
tests:
    test2:
        name:test2
        location:0
    test4: 
        name: test4
        location: 0

如何像上面的形式解析?

尽管您可以使用 PyYAML 执行此操作,但您必须限制 你自己到 YAML 1.1。对于这种读-修改-写你应该 使用 ruamel.yaml (免责声明:我是该包的作者)。不是 仅支持 YAML 1.2,它还保留任何评论、标签 和锚点名称,以防它们出现在您的源代码中并且可以保留 标量、文字和折叠样式等周围的引号,如果您需要的话。

另请注意,您的输出是无效的 YAML,您不能 多行普通(即不带引号的)标量是(块样式)的关键 映射。你必须写:

"testname:test
tests":

但我假设你的意思是根级别映射的两个键:

testname: test
tests:

假设您的输入是 input.yaml:

testname: testname
testall:
    test1:
        name: name1    # this is just the first name
        location: 0
    test2: 
        name: "name2"  # quotes added for demo purposes
        location: 2
    test3: 
        name: name3    # as this has the same location as name1 
        location: 0    # these should be together
    test4: 
        name: name4    # and this one goes with name2
        location: 2
locations:
    - 0
    - 2

你可以做到:

import sys
from pathlib import Path
import ruamel.yaml

in_file = Path('input.yaml')


yaml = ruamel.yaml.YAML()
yaml.indent(mapping=4, sequence=6, offset=4)  # this matches your input
yaml.preserve_quotes = True
data = yaml.load(in_file)

for loc in data['locations']:
    out_name = f'location{loc}.yaml'
    tests = {}
    d = ruamel.yaml.comments.CommentedMap(dict(testname="test", tests=tests))
    d.yaml_set_start_comment(out_name)
    testall = data['testall']
    for test in testall:
        if loc == testall[test]['location']:
           tests[test] = testall[test]
           tests[test]['location'] = 0
    # since you set location to zero and this affects data, make sure to remove 
    # the items. This will prevent things from going wrong in case the
    # locations sequence does have zero, but not as its first number
    for key in tests:
         del testall[key]
    yaml.dump(d, Path(out_name))

这给出 location0.yaml:

# location0.yaml
testname: test
tests:
    test1:
        name: name1    # this is just the first name
        location: 0
    test3:
        name: name3    # as this has the same location as name1 
        location: 0    # these should be together

location2.yaml:

# location2.yaml
testname: test
tests:
    test2:
        name: "name2"  # quotes added for demo purposes
        location: 0
    test4:
        name: name4    # and this one goes with name2
        location: 0