处理带有属性的Python yaml文件标签

问题描述 投票:0回答:1
!Employee old:
    id:   some_id 
    name: John
cfg:
    param: "abc"

我需要加载更新,并保存一个yaml文件,其中包含与上面类似的结构。

我尝试使用add_constructor和add_representer。当标签单独放置时,这有效。

!Employee 
old:
    id:   some_id 
    name: John
cfg:
    param: "abc"

不过我想避免对原始文件进行预处理。我也没有鲁梅尔(在其他帖子上建议)。

在下面的示例中,yaml 没有看到扫描整个结构,即不包括标签下的映射部分。

#================================================== ====================

import yaml


# Custom class to handle !Employee old: name: Peter
class Employee:
    def __init__(self, status, data=None):
        self.status = status  # e.g., "old"
        self.data = data or {}  # e.g., {'name': 'Peter'}

    def __repr__(self):
        return f"!Employee {self.status}: {self.data}"


# Custom constructor for the !Employee tag
def employee_constructor(loader, node):
    # Check if the node is a MappingNode
    print(f"EMPL_CONST node: {node}")
    if isinstance(node, yaml.MappingNode):
        # Expecting only one key-value pair (e.g., "old": {"name": "Peter"})
        print(f"EMPL_CONST node: {node.value[0]}")
        first_key_node, value_node = node.value[0]

        # The key is the scalar "old"
        status = loader.construct_scalar(first_key_node)

        # The value is the mapping part {"name": "Peter"}
        data = loader.construct_mapping(value_node)

        # Return an Employee object with the status and data
        return Employee(status, data)

    raise yaml.constructor.ConstructorError(None, None, f"Unexpected node type: {type(node)}", node.start_mark)


# Custom representer for the !Employee tag
def employee_representer(dumper, data):
    # Create a tag with the status (e.g., !Employee old)

    tag = f"!Employee"
    return dumper.represent_mapping(tag, {data.status: data.data})

# Register the custom constructor and representer
yaml.add_constructor('!Employee', employee_constructor)
yaml.add_representer(Employee, employee_representer)

def load_yaml(file_path):
    with open(file_path, 'r') as file:
        data = yaml.load(file, Loader=yaml.FullLoader)
        # print(data)
    return data
def save_yaml(file_path, data):
    with open(file_path, 'w') as file:
        yaml.dump(data, file, default_flow_style=False)

# Testing the code
if __name__ == "__main__":
    yaml_string = """
    !Employee old:
        name: Some_Name
        id: some_id
    
    cfg:
        param : 'abc'
    """

    # Load the YAML data`your text`
    data = yaml.load(yaml_string, Loader=yaml.FullLoader)
    # data = load_yaml("input3.yaml")
    print("Loaded YAML data:")
    print(data)

    # Modify the data (optional)
    data['cfg']['param'] = 'xyz'

    # Dump the data back to YAML format
    output = yaml.dump(data, default_flow_style=False)
    print("\nModified YAML output:")
    print(output)
    save_yaml("output3.yaml", data)
python nested yaml tags
1个回答
0
投票

您使用了错误的库来执行这种往返(加载/修改/保存)操作。 使用 PyYAML,除了必须编写自定义加载程序和转储程序之外,您还将丢失引用、注释。 另外 PyYAML 只能处理 YAML 1.1 的一个子集,该子集已于 15 年前被取代。

我建议您尝试 ruamel.yaml (双关语:我是该包的作者):

import sys
import ruamel.yaml

yaml_str = """\
!Employee old:
    [42, 13]:   some_id 
    name: John    # this stays the same
cfg:
    param: "abc"  # this has to change
"""
    
yaml = ruamel.yaml.YAML()
yaml.indent(mapping=4)
yaml.preserve_quotes = True
data = yaml.load(yaml_str)
data['cfg']['param'] = 'xyz'
yaml.dump(data, sys.stdout)

给出:

!Employee old:
    [42, 13]: some_id
    name: John    # this stays the same
cfg:
    param: "xyz"  # this has to change
© www.soinside.com 2019 - 2024. All rights reserved.