PyYAML - 序列化作为对象成员的类(类型/类引用)

时间:2017-08-13 22:45:21

标签: python serialization yaml pyyaml

短版:如何序列化作为对象成员的类(类引用,即不是对象)?(参见:example)?

长版

我在工作中一直在使用这个问题的答案:How can I ignore a member when serializing an object with PyYAML?

所以,我目前的实现是:

class SecretYamlObject(yaml.YAMLObject):
    """Helper class for YAML serialization.
    Source: https://stackoverflow.com/questions/22773612/how-can-i-ignore-a-member-when-serializing-an-object-with-pyyaml """

    def __init__(self, *args, **kwargs):
        self.__setstate__(self, kwargs) #Default behavior, so one could just use setstate
        pass

    hidden_fields = []
    @classmethod
    def to_yaml(cls,dumper,data):
        new_data = copy(data)
        for item in cls.hidden_fields:
            if item in new_data.__dict__:
               del new_data.__dict__[item]
        res = dumper.represent_yaml_object(cls.yaml_tag, new_data, cls, flow_style=cls.yaml_flow_style)
        return res

到目前为止,这对我来说一直很好,因为到目前为止我只需要隐藏记录器:

class EventManager(SecretYamlObject):
    yaml_tag = u"!EventManager"
    hidden_fields = ["logger"]

    def __setstate__(self, kw): # For (de)serialization
        self.logger = logging.getLogger(__name__)
        self.listeners = kw.get("listeners",{})
        #...
        return


    def __init__(self, *args, **kwargs):
        self.__setstate__(kwargs)
        return

然而,当我尝试序列化非平凡对象时会出现一个不同的问题(如果Q直接来自对象,这很好,但是从yaml.YAMLObject它失败了"无法挑选int对象& #34)。请参阅示例

class Q(SecretYamlObject): #works fine if I just use "object"
    pass

class A(SecretYamlObject):
    yaml_tag = u"!Aobj"
    my_q = Q
    def __init__(self, oth_q):
        self.att = "att"
        self.oth_q = oth_q
        pass
    pass

class B(SecretYamlObject):
    yaml_tag = u"!Bobj"
    my_q = Q
    hidden_fields = ["my_q"]
    def __init__(self, oth_q):
        self.att = "att"
        self.oth_q = oth_q
        pass
    pass

class C(SecretYamlObject):
    yaml_tag = u"!Cobj"
    my_q = Q
    hidden_fields = ["my_q"]

    def __init__(self, *args, **kwargs):
        self.__setstate__(kwargs)
        pass

    def __setstate__(self, kw):
        self.att = "att"
        self.my_q = Q
        self.oth_q = kw.get("oth_q",None)
        pass

    pass

a = A(Q)
a2 = yaml.load(yaml.dump(a))

b = B(Q)
b2 = yaml.load(yaml.dump(b))

c = C(my_q=Q)
c2 = yaml.load(yaml.dump(c))
c2.my_q
c2.oth_q

A和B给予"不能腌制对象"错误,而C没有初始化oth_q(因为没有关于它的信息)。

问题:如何保留有关哪个类引用的信息?

(我需要保持类引用以便能够创建该类型的对象 - 替代它也可能有效)

1 个答案:

答案 0 :(得分:1)

加载转储的YAML时,通常不需要保留有关需要实例化哪个类的信息。这就是存储在!XObj文件中的标记信息。

如果隐藏对某个类的对象的引用,通过不转储引用它的属性,然后在加载时遇到问题实例化该对象(因为你不知道它的类),你正在做有问题。在这种情况下,您应该隐藏引用对象的内部,而不是引用该对象的属性。你可以,例如使用!XObj null转储引用的对象。

通过隐藏内部,您将拥有适当的标记,指向正确的类以在加载时创建对象。根据有限的null信息,您必须根据有限的yaml.YAMLObject信息确定您的程序内容与该对象的内容。

警告:您应该认真重新考虑使用load()的方式。您正在使用记录为不安全的safe_load(),如果您不能保证100%控制,现在和将来的任何时候,您的YAML输入,您可能会丢失您的驱动器的内容,保密你试图隐藏的对象,或者更糟。你应该使用{{1}}或者远离使用像PyYAML这样的库,它默认是不安全的。