[英]Python JSON serialize excluding certain fields
概括
我有一个 Python 对象层次结构,我想使用 JSON 进行序列化(仅通过https://docs.python.org/3/library/json.html ,不使用任何额外的第三方库)。 我想排除某些字段/属性/子对象。 我发现很难找到一个关于如何实现这一目标的简单答案?
例子
我将有一个派生类实例,结果如下:
class MyItemClass(BaseItemClass):
self.saveThisProperty = 999
self.dontSaveThisProperty = "Something"
self.saveThisObject = ObjectType1()
self.dontSaveThisObject = ObjectType2()
如果我要序列化为 XML,我希望它看起来像
<MyItemClass>
<saveThisProperty>999</saveThisProperty>
<saveThisObject>
...
</saveThisObject>
</MyItemClass>
请注意,我只序列的某些属性/子对象,我不想要序列整个BaseItemClass
从我的类实例的。
在 XML 中我很好。 我知道如何按照我想要的方式输出一些 XML,或者输出到我最后保存的临时内存文档,或者通过将单个节点/元素逐步输出到流中。 我不必序列化所有内容。 例如
xmlStream.writeStartElement("MyItemClass")
xmlStream.writeElementWithValue("saveThisProperty", 999)
xmlStream.writeStartElement("saveThisObject")
...
xmlStream.writeEndElement("saveThisObject")
xmlStream.writeEndElement("MyItemClass")
对于 JSON,我不能这样做,对吗? 我是否必须通过将我想要的属性/子对象复制到其中然后 JSON 序列化来创建一些新的“独立”对象层次结构(没有从BaseClass
派生)?
我确实看到有json.dump(default = ...)
,但它说:
如果指定,则 default 应该是一个函数,该函数为无法以其他方式序列化的对象调用。 它应该返回对象的 JSON 可编码版本
然而,并不是原始对象不能在默认情况下序列化 Python->JSON,而是我不想要那种默认的、序列化的所有行为,我想要我的“选择性”行为。
我是 OP。 为了清楚起见,我在这里发布了我最终用于我的案例的内容。
我已将@Sina Rezaei 在此线程中的帖子标记为已接受的解决方案,因为那(他帖子的最后一部分)和@snakechamerb 的评论启发了我了解需要什么。
我的解决方案的轮廓看起来像:
class ModelScene(QGraphicsScene):
# Serialize whole scene to JSON into stream
def json_serialize(self, stream) -> None:
# Get `json.dump()` to call `ModelScene.json_serialize_dump_obj()` on every object to be serialized
json.dump(self, stream, indent=4, default=ModelScene.json_serialize_dump_obj)
# Static method to be called from `json.dump(default=ModelScene.json_serialize_dump_obj)`
# This method is called on every object to be dumped/serialized
@staticmethod
def json_serialize_dump_obj(obj):
# if object has a `json_dump_obj()` method call that...
if hasattr(obj, "json_dump_obj"):
return obj.json_dump_obj()
# ...else just allow the default JSON serialization
return obj
# Return dict object suitable for serialization via JSON.dump()
# This one is in `ModelScene(QGraphicsScene)` class
def json_dump_obj(self) -> dict:
return {
"_classname_": self.__class__.__name__,
"node_data": self.node_data
}
class CanvasModelData(QAbstractListModel):
# Return dict object suitable for serialization via JSON.dump()
# This one is class CanvasModelData(QAbstractListModel)
def json_dump_obj(self) -> dict:
_data = {}
for key, value in self._data.items():
_data[key] = value
return {
"_classname_": self.__class__.__name__,
"data_type": self.data_type,
"_data": _data
}
def json_dump_obj(self) -> dict:
方法。json.dump(self, stream, default=ModelScene.json_serialize_dump_obj)
通过静态方法ModelScene.json_serialize_dump_obj
导致每个访问的节点被增量序列化为流。 如果可用,则调用我的obj.json_dump_obj()
,否则调用基本对象类型的默认 JSON 序列化。有趣的是,我遇到了一个和我有同样担忧的人。 来自python 中的 json.dump() 和 json.dumps() 有什么区别? ,解决方案https://stackoverflow.com/a/57087055/489865 :
在内存使用和速度方面。
当您调用
jsonstr = json.dumps(mydata)
它首先在内存中创建数据的完整副本,然后才将它file.write(jsonstr)
到磁盘。 因此,这是一种更快的方法,但如果您要保存大量数据,则可能会出现问题。当您调用
json.dump(mydata, file)
-- 没有's'
,不会使用新内存,因为数据是按块转储的。 但整个过程大约慢了 2 倍。来源:我检查了
json.dump()
和json.dumps()
的源代码,并且还测试了使用time.time()
测量时间并观察 htop 中的内存使用情况的变体。
针对您的情况,我可以想到三种解决方案:
解决方案一:使用Pykson第三方库,将你想要序列化的字段定义为pykson字段。
样本:
class MyItemClass(pykson.JsonObject):
saved_property = pykson.IntegerField()
my_object = MyItemClass(saved_property=1, accept_unknown=True)
my_object.unsaved_property = 2
pykson.Pykson().to_json(my_object)
免责声明:我是 pykson 库的开发人员。
解决方案 2:第二种解决方案是使用带有自定义默认解串器的包装类。
class ObjectWrapper:
def __init__(self, value, should_serialize=False)
self.value = value
self.should_serialize = should_serialize
def default_handler(obj):
if isinstance(obj, ObjectWrapper):
if obj.should_serialize:
return obj.value
else:
return None
else:
raise TypeError
json.dump(default=default_handler)
解决方案 3:这可能是一个坏主意,但是如果您有一个很深的层次结构,您还可以向将被序列化的 allc 类添加一个函数,并使用此函数获取字典并轻松将字典转换为 json。
class MyChildClass:
def __init__(self, serialized_property, not_serialized_property):
self.serialized_property = serialized_property
self.not_serialized_property = not_serialized_property
def to_dict(self):
# only add serialized property here
return {
"serialized_property": self.serialized_property
}
class MyParentClass:
def __init__(self, child_property, some_other_property):
self.child_property = child_property
self.some_other_property = some_other_property
def to_dict(self):
return {
'child_property': self.child_property.to_dict(),
'some_other_property': self.some_other_property
}
my_child_object = MyChildClass(serialized_property=1, not_serialized_property=2)
my_parent_object = MyParentClass(child_property=my_child_object, some_other_property='some string here')
json.dumps(my_parent_object.to_dict())
或者您可以使用默认处理程序实现相同的结果:
class MyChildClass:
def __init__(self, serialized_property, not_serialized_property):
self.serialized_property = serialized_property
self.not_serialized_property = not_serialized_property
def to_dict(self):
# only add serialized property here
return {
"serialized_property": self.serialized_property
}
class MyParentClass:
def __init__(self, child_property, some_other_property):
self.child_property = child_property
self.some_other_property = some_other_property
def to_dict(self):
return {
'child_property': self.child_property,
'some_other_property': self.some_other_property
}
def handle_default(obj):
if isinstance(obj, MyChildClass):
return obj.to_dict()
elif isinstance(obj, MyParentClass):
return obj.to_dict()
return None
my_child_object = MyChildClass(serialized_property=1, not_serialized_property=2)
my_parent_object = MyParentClass(child_property=my_child_object, some_other_property='some string here')
json.dumps(my_parent_object, default=handle_default)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.