Django HttpResponse 无法反序列化

79 阅读2分钟

在使用自定义 pickler 将 Django HttpResponse 对象序列化后,在反序列化的过程中遇到了 UnpicklingError: NEWOBJ class argument isn't a type object 错误。

f75b0c2e5ac6bb734832a5f5a5ab2a6.png

解决方案

Pickle 的序列化和反序列化过程涉及两个步骤:序列化和反序列化。序列化是将对象转换为可存储或传输的格式,反序列化是从存储或传输的格式中重新创建对象。在自定义 pickler 中,允许将一些无法正常序列化的对象(例如套接字或文件)替换为它们的字符串表示形式。

在尝试反序列化 Django HttpResponse 对象时,遇到了 UnpicklingError: NEWOBJ class argument isn't a type object 错误。

错误原因

在使用自定义 pickler 将对象序列化时,对象的类型信息也会被序列化。在反序列化时,需要使用相同的类型信息来重新创建对象。但是,在自定义 pickler 中,将一些无法正常序列化的对象替换为它们的字符串表示形式,导致反序列化时无法找到相应的类型信息,从而引发了错误。

解决方法

为了解决此问题,需要将自定义 pickler 修改为在序列化时保留对象的类型信息。可以使用 pickle.Pickler 的 persistent_id 属性来实现,该属性允许在序列化时指定对象的替代ID。

以下是修改后的代码:

from cPickle import Pickler, Unpickler, UnpicklingError

class FilteredObject:
    def __init__(self, about):
        self.about = about
    def __repr__(self):
        return 'FilteredObject(%s)' % repr(self.about)

class MyPickler(object):
    def __init__(self, file, protocol=2):
        pickler = Pickler(file, protocol)
        pickler.persistent_id = self.persistent_id
        self.dump = pickler.dump
        self.clear_memo = pickler.clear_memo

    def persistent_id(self, obj):
        if not hasattr(obj, '__getstate__') and not isinstance(obj,
        (basestring, bool, int, long, float, complex, tuple, list, set, dict)):
            return type(obj), str(obj)
        else:
            return None

class MyUnpickler(object):
    def __init__(self, file):
        unpickler = Unpickler(file)
        unpickler.persistent_load = self.persistent_load
        self.load = unpickler.load
        self.noload = unpickler.noload

    def persistent_load(self, obj_id):
        if isinstance(obj_id, tuple):
            obj_type, obj_value = obj_id
            return obj_type(obj_value)
        else:
            raise UnpicklingError('Invalid persistent id')

###### serialize to file

f = open('test.txt','wb')
p = MyPickler(f)
p.dump(data)
f.close()

###### unserialize from file

f = open('test.txt','rb')
pickled_data = f.read()
f.seek(0)
u = MyUnpickler(f)
data = u.load()    

在修改后的代码中,在 persistent_id 方法中,如果遇到无法正常序列化的对象,则会使用 pickle.Pickler 的 persistent_id 属性来指定对象的替代ID。这个替代ID是一个元组,第一个元素是对象的类型,第二个元素是对象的字符串表示形式。

在反序列化时,如果遇到带有替代ID的对象,则会使用 persistent_load 方法来重新创建对象。在 persistent_load 方法中,会根据对象的类型和字符串表示形式来重新创建对象。

这样,就可以解决反序列化时遇到的 UnpicklingError: NEWOBJ class argument isn't a type object 错误。