將自訂不可序列化物件序列化為JSON 的預設方法是子類化json.JSONEncoder 並將自訂編碼器傳遞給json.dumps()。這通常如下所示:
<code class="python">class CustomEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, Foo): return obj.to_json() return json.JSONEncoder.default(self, obj) print(json.dumps(obj, cls=CustomEncoder))</code>
但是,如果您想使用預設編碼器使物件可序列化怎麼辦?查看 json 模組的原始碼後,似乎直接擴展編碼器無法滿足此要求。
相反,您可以在套件的 __init__.py 初始化腳本中使用一種稱為「monkey-patching」的技術。這會影響所有後續的 JSON 模組序列化,因為模組通常只會載入一次,結果會快取在 sys.modules 中。
該補丁將修改預設 JSON 編碼器的預設方法以檢查唯一的「to_json」方法並利用它對找到的物件進行編碼。
為了簡單起見,這裡有一個作為獨立模組實現的示例:
<code class="python"># Module: make_json_serializable.py from json import JSONEncoder def _default(self, obj): return getattr(obj.__class__, "to_json", _default.default)(obj) _default.default = JSONEncoder.default # Save unmodified default. JSONEncoder.default = _default # Replace it.</code>
使用此補丁很簡單:只需導入模組即可應用猴子-patch.
<code class="python"># Sample client script import json import make_json_serializable # apply monkey-patch class Foo(object): def __init__(self, name): self.name = name def to_json(self): # New special method. """Convert to JSON format string representation.""" return '{"name": "%s"}' % self.name foo = Foo('sazpaz') print(json.dumps(foo)) # -> '{"name": "sazpaz"}'</code>
要保留物件類型訊息,to_json 方法可以將其包含在傳回的字串中:
<code class="python">def to_json(self): """Convert to JSON format string representation.""" return '{"type": "%s", "name": "%s"}' % (self.__class__.__name__, self.name)</code>
這會產生包含類別名稱的JSON:
{"type": "Foo", "name": "sazpaz"}
更強大的方法是讓替換預設方法自動序列化大多數Python 對象,包括使用者定義的類別實例,而不需要唯一的方法。
在研究了幾種替代方案之後,以下基於pickle 的方法似乎最接近這個理想:
<code class="python"># Module: make_json_serializable2.py from json import JSONEncoder import pickle def _default(self, obj): return {"_python_object": pickle.dumps(obj)} JSONEncoder.default = _default # Replace with the above.</code>
雖然並非所有內容都可以進行pickle(例如擴展類型),但pickle 提供了透過協定處理它們的方法使用獨特的方法。但是,這種方法涵蓋了更多情況。
使用 pickle 協定透過在遇到「_python_object」時向 json.loads() 提供自訂 object_hook 函數參數來簡化重建原始 Python 物件字典中的 key。
<code class="python">def as_python_object(dct): try: return pickle.loads(str(dct['_python_object'])) except KeyError: return dct pyobj = json.loads(json_str, object_hook=as_python_object)</code>
這可以簡化為包裝函數:
<code class="python">json_pkloads = functools.partial(json.loads, object_hook=as_python_object) pyobj = json_pkloads(json_str)</code>
此程式碼在 Python 3 中不起作用,因為 json.dumps() 傳回一個 bytes 物件JSONEncoder 無法處理。但是,此方法經過以下修改仍然有效:
<code class="python">def _default(self, obj): return {"_python_object": pickle.dumps(obj).decode('latin1')} def as_python_object(dct): try: return pickle.loads(dct['_python_object'].encode('latin1')) except KeyError: return dct</code>
以上是如何在不子類化`json.JSONEncoder`的情況下使自訂物件JSON可序列化?的詳細內容。更多資訊請關注PHP中文網其他相關文章!