簡體   English   中英

Python JSON序列化排除某些字段

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM