将复杂python对象序列化/反序列化为JSON的最佳实践是什么,这将考虑子类化并防止相同对象的多个副本(假设我们知道如何区分同一类的不同实例)以存储多个次?
简而言之,我正在写一个小型科学图书馆并希望人们使用它。但看完Raymond Hettinger讲话后Python's Class Development Toolkit我已经决定实施子类化感知行为对我来说是一个很好的练习。到目前为止一切正常,但现在我点击了JSON序列化任务。
到目前为止,我已经环顾四周,发现了以下有关Python中JSON序列化的内容:
我遇到的两个主要障碍是考虑可能的子类化,每个实例的单个副本。
在纯python中多次尝试解决它之后,没有对对象的JSON表示进行任何更改,我终于理解了,在反序列化JSON的时候,现在有办法知道什么是实例阶级继承人之前被连载了。所以有些人会提到它,我最终得到了类似的东西:
class MyClassJSONEncoder(json.JSONEncoder):
@classmethod
def represent_object(cls, obj):
"""
This is a way to serialize all built-ins as is, and all complex objects as their id, which is hash(obj) in this implementation
"""
if isinstance(obj, (int, float, str, Boolean)) or value is None:
return obj
elif isinstance(obj, (list, dict, tuple)):
return cls.represent_iterable(obj)
else:
return hash(obj)
@classmethod
def represent_iterable(cls, iterable):
"""
JSON supports iterables, so they shall be processed
"""
if isinstance(iterable, (list, tuple)):
return [cls.represent_object(value) for value in iterable]
elif isinstance(iterable, dict):
return [cls.represent_object(key): cls.represent_object(value) for key, value in iterable.items()]
def default(self, obj):
if isinstance(obj, MyClass):
result = {"MyClass_id": hash(obj),
"py__class__": ":".join([obj.__class__.__module, obj.__class__.__qualname__]}
for attr, value in self.__dict__.items():
result[attr] = self.represent_object(value)
return result
return super().default(obj) # accounting for JSONEncoder subclassing
这里的子类化会计在
中完成"py__class__": ":".join([obj.__class__.__module, obj.__class__.__qualname__]
JSONDecoder的实现如下:
class MyClassJSONDecoder(json.JSONDecoder):
def decode(self, data):
if isinstance(data, str):
data = super().decode(data)
if "py__class__" in data:
module_name, class_name = data["py__class__"].split(":")
object_class = getattr(importlib.__import__(module_name, fromlist=[class_name]), class_name)
else:
object_class = MyClass
data = {key, value for key, value in data.items() if not key.endswith("_id") or key != "py__class__"}
return object_class(**data)
可以看出,这里我们用" py__class __"来解释可能的子类化。在对象的JSON表示中属性,如果不存在这样的属性(可能是这种情况,如果JSON是由另一个程序生成的,比如在C ++中,他们只想向我们传递关于普通MyClass对象的信息,并且&#&# 39;真正关心继承)创建MyClass
实例的默认方法
被追求。顺便说一句,这就是为什么不能创建所有对象的单个JSONDecoder的原因:如果没有指定py__class__
,它必须有一个默认的类值来创建。
就每个实例的单个副本而言,这是通过以下事实来完成的:该对象使用特殊的JSON密钥myclass_id
进行序列化,并且所有属性值都被序列化为基元(lists
,保留tuples
,dicts
和内置,而当复杂对象是某个属性的值时,只存储其哈希值)。这种存储对象哈希的方法允许人们准确地序列化每个对象一次,然后,知道要从json表示解码的对象的结构,它可以查找相应的对象并最终分配它们。为了简单说明,可以观察到以下示例:
class MyClass(object):
json_encoder = MyClassJSONEncoder()
json_decoder = MyClassJSONDecoder()
def __init__(self, attr1):
self.attr1 = attr1
self.attr2 = [complex_object_1, complex_object_2]
def to_json(self, top_level=None):
if top_level is None:
top_level = {}
top_level["my_class"] = self.json_encoder.encode(self)
top_level["complex_objects"] = [obj.to_json(top_level=top_level) for obj in self.attr2]
return top_level
@classmethod
def from_json(cls, data, class_specific_data=None):
if isinstance(data, str):
data = json.loads(data)
if class_specific_data is None:
class_specific_data = data["my_class"] # I know the flat structure of json, and I know the attribute name, this class will be stored
result = cls.json_decoder.decode(class_spcific_data)
# repopulate complex valued attributes with real python objects
# rather than their id aliases
complex_objects = {co_data["ComplexObject_id"]: ComplexObject.from_json(data, class_specific_data=co_data) for co_data in data["complex_objects"]]
result.complex_objects = [c_o for c_o_id, c_o in complex_objects.items() if c_o_id in self.complex_objects]
# finish such repopulation
return result
这是否是正确的方法?有更强大的方式吗?我是否错过了在这种特殊情况下实施的编程模式?
我真的很想了解实现JSON序列化的最正确和pythonic方法是什么,该序列化会考虑子类化并且还会阻止存储同一对象的多个副本。