Python JSON 序列化排除某些字段

Jon*_*ave 5 python serialization json

概括

\n\n

我有一个 Python 对象层次结构,我想使用 JSON 进行序列化(仅通过https://docs.python.org/3/library/json.html,不使用任何额外的第三方库)。我想排除某些字段/属性/子对象。我发现很难找到关于如何实现这一目标的简单答案?

\n\n

例子

\n\n

我将得到一个如下所示的派生类实例:

\n\n
class MyItemClass(BaseItemClass):\n    self.saveThisProperty = 999\n    self.dontSaveThisProperty = "Something"\n    self.saveThisObject = ObjectType1()\n    self.dontSaveThisObject = ObjectType2()\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果我序列化为 XML,我希望它看起来像

\n\n
<MyItemClass>\n    <saveThisProperty>999</saveThisProperty>\n    <saveThisObject>\n        ...\n    </saveThisObject>\n</MyItemClass>\n
Run Code Online (Sandbox Code Playgroud)\n\n

请注意,我只序列化某些属性/子对象,并且我不想序列BaseItemClass化派生类实例的整个对象。

\n\n

在 XML 中我很好。我知道如何在进行所需操作时输出 XML 片段,要么输出到最后保存的临时内存文档中,要么将各个节点/元素增量输出到流中。我不必序列化所有内容。例如

\n\n
xmlStream.writeStartElement("MyItemClass")\n    xmlStream.writeElementWithValue("saveThisProperty", 999)\n    xmlStream.writeStartElement("saveThisObject")\n        ...\n    xmlStream.writeEndElement("saveThisObject")\nxmlStream.writeEndElement("MyItemClass")\n
Run Code Online (Sandbox Code Playgroud)\n\n

对于 JSON 我不能这样做,可以吗?我是否必须创建一些新的“独立”对象层次结构(没有派生自BaseClass通过仅将我想要的属性/子对象复制到其中,然后进行 JSON 序列化来

\n\n

我确实看到有json.dump(default = ...),但上面写着:

\n\n
\n

如果指定,默认值应该是一个为可以\xe2\x80\x99t否则被序列化的对象调用的函数。它应该返回对象的 JSON 可编码版本

\n
\n\n

不过,并不是说原始对象默认Python->JSON不能序列化,是我不想那种默认的、序列化一切的行为,我想要我的“选择性”行为。

\n

Jon*_*ave 5

我是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
      }
Run Code Online (Sandbox Code Playgroud)
  • 每个“复杂”类都定义一个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()基本对象类型的 if available, else 默认 JSON 序列化。

有趣的是,我遇到了和我有同样担忧的人。来自Python中的json.dump()和json.dumps()有什么区别?,解决方案/sf/answers/3996093881/

在内存使用和速度方面。

当您首先调用jsonstr = json.dumps(mydata)它时,它会在内存中创建数据的完整副本,然后才将file.write(jsonstr)其复制到磁盘。因此,这是一种更快的方法,但如果您要保存大量数据,则可能会出现问题。

当您调用json.dump(mydata, file)--without时's',不会使用新内存,因为数据是按块转储的。但整个过程慢了大约2倍。

json.dump()来源:我检查了和 的源代码,json.dumps()还测试了两个变体,用 htop 测量时间time.time()并观察内存使用情况。


Sin*_*aei 2

针对你的情况我可以想到三种解决方案:

解决方案1: 使用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)
Run Code Online (Sandbox Code Playgroud)

免责声明:我是 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)
Run Code Online (Sandbox Code Playgroud)

解决方案 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())
Run Code Online (Sandbox Code Playgroud)

或者您可以使用默认处理程序获得相同的结果:

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)
Run Code Online (Sandbox Code Playgroud)