如何在PyYaml中处理标签/类向后兼容?

时间:2019-07-08 09:50:47

标签: python pyyaml

假设我有一个类MyClass驻留在软件包my_package中。将此类数据转储到YAML可能会产生:

!!python/object:my_package.MyClass
my_field_1: "foo"
my_field_2: "bar"

可以使用默认加载程序反序列化此数据。但是我们如何处理重构包/类名?例如,如果我们将包重命名为my_new_package,则代码将无法反序列化现有的YAML文件,就像人们期望的那样:

yaml.constructor.ConstructorError: while constructing a Python object
cannot find 'MyClass' in the module 'my_package'

如何向YAML加载程序添加向后兼容性,以便仍然可以加载旧数据?


我的第一个尝试是自定义加载程序并注册旧标签名称以实现向后兼容:

class CustomLoader(yaml.SafeLoader):
    pass

def my_class_loader(loader, node):
    # to be implemented

CustomLoader.add_constructor("!!python/object:my_package.MyClass", my_class_loader)

data = yaml.load(f, Loader=CustomLoader)

不幸的是,PyYaml从未调用此自定义负载。是否有另一种方法可以将旧标签注入到加载过程中?

1 个答案:

答案 0 :(得分:0)

我找到了一个可行的解决方案,但是我不确定这是否是解决问题的最优雅的方法:

在执行构造函数查找之前,PyYaml似乎在内部将存储的标签!!python/object:my_package.MyClass转换为tag:yaml.org,2002:python/object:my_package.MyClass

因此,为了为旧标签名注册自定义加载程序,可以执行以下操作:

# Example class
class MyClass(object):
    def __init__(my_field_1, my_field_2):
        self.my_field_1 = my_field_1
        self.my_field_2 = my_field_2

    @staticmethod
    def from_dict(d):
        return MyClass(
            my_field_1=d["my_field_1"],
            my_field_2=d["my_field_2"],
        )

# Build a custom loader
class CustomLoader(yaml.SafeLoader):
    pass

def my_class_loader(loader, node):
    mapping = loader.construct_mapping(node)
    return MyClass.from_dict(mapping)

# Register the old tag name
CustomLoader.add_constructor(
    "tag:yaml.org,2002:python/object:my_package.MyClass", 
    my_class_loader)

# Load using custom loader
data = yaml.load(f, Loader=CustomLoader)