Python - yaml 由另一个 yaml 文件覆盖别名键值

问题描述 投票:0回答:1

我有两个yaml文件,A.yaml(基础)和B.yaml(覆盖),我想用B文件覆盖A文件别名键值。

A.yaml:

A1: &A1
  AA:
    AAA: true
    AAB: true
  AB: 
    ABA: [1, 1, 1]
    ABB: true 
A2:
  <<: *A1
  AB:
    ABA: [1, 2, 1] # overwrite anchor value by alias.

和B.yaml如下:

A2:
  AB: 
    ABA: [2, 2, 2] # overwrite alias value.

覆盖的输出 yaml 将是这样的:

A1:
  AA:
    AAA: true
    AAB: true
  AB: 
    ABA: [1, 1, 1]
    ABB: true 

A2:
  AA:
    AAA: true
    AAB: true
  AB: 
    ABA: [2, 2, 2] # modified alias key
    ABB: true      # keep the original keys

我做过这样的东西,但是没法实现,有没有办法用PyYAML来满足要求?

import yaml

def _overrideCfg(data, over_data):
    """Override key values in data using over_data"""
    if isinstance(data, dict) and isinstance(over_data, dict):
        for key, value in over_data.items():
            if key in data:
                if isinstance(data[key], dict) and isinstance(value, dict):
                    _overrideCfg(data[key], value)
                else:
                    # Override the value in the data dictionary
                    data[key] = value
            else:
                # Add the new key-value pair to the data dictionary
                data[key] = value

        # Serialize the updated data dictionary to YAML
        output = yaml.dump(data, default_flow_style=False)
        return output
    else:
        raise TypeError("Both data and over_data must be dictionaries")
# Load the YAML files
with open('A.yaml') as f:
    data = yaml.safe_load(f)

with open('B.yaml') as f:
    over_data = yaml.safe_load(f)

# Override the values in data using over_data
data = _overrideCfg(data, over_data)

# Print the updated YAML
with open('output.yaml', 'w') as f:
    f.write(data)
python yaml alias overwrite pyyaml
1个回答
0
投票

加载后

A.yaml
你没有根级键
ABB
下的键
A2
,这就是PyYAML中合并的工作方式,你也 没有对原始锚定映射的引用。

所以加载后以某种方式深度合并

B.yaml
,永远不会让你找回那把钥匙。 相反,您可以使用另一个或额外的锚点/别名:

import sys
from pathlib import Path
import ruamel.yaml

path_a = Path('A.yaml')
path_b = Path('B.yaml')

path_a.write_text("""\
A1: &A1
  AA:
    AAA: true
    AAB: true
  AB: &another 
    ABA: [1, 1, 1]
    ABB: true 
A2:
  <<: *A1
  AB:
    <<: *another
    ABA: [1, 2, 1] # overwrite anchor value by alias.
""")

def overwrite(data, update):
    for k, v in update.items():
        if isinstance(v, dict):
            overwrite(data[k], v)
        else:
            data[k] = v
    
yaml = ruamel.yaml.YAML()
data = yaml.load(path_a)
assert data['A2']['AA']['AAA']
assert data['A2']['AB']['ABB']
assert data['A2']['AB']['ABA'] == [1, 2, 1]

update = yaml.load(path_b)
overwrite(data, update)
assert data['A2']['AB']['ABB']
assert data['A2']['AB']['ABA'] == [2, 2, 2]
yaml.dump(data, sys.stdout)

给出:

A1: &A1
  AA:
    AAA: true
    AAB: true
  AB: &another
    ABA: [1, 1, 1]
    ABB: true
A2:
  <<: *A1
  AB:
    <<: *another
    ABA: [2, 2, 2] # overwrite anchor value by alias.

在上面

B.yaml
是你指定的。

如果您无法更改

A.yaml
,可以从原始
A.yaml
开始并更新 加载文档就像加载一样 来自我一直在使用的
A.yaml
。 PyYAML 不能这样做(因为它在加载期间解析合并键),并且 除此之外,它仅支持 YAML 1.1 规范的一个子集,该规范已于 2009 年(十四年前)过时。 IMO 它不应该用于新项目。

© www.soinside.com 2019 - 2024. All rights reserved.