シリアル化できないカスタム オブジェクトを 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」と呼ばれる手法を使用できます。通常、モジュールは 1 回だけロードされ、結果は sys.modules にキャッシュされるため、これは後続のすべての JSON モジュールのシリアル化に影響します。
このパッチは、固有の "to_json" メソッドをチェックするようにデフォルトの 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」が発生したときにカスタム object_hook 関数の引数を json.loads() に提供することで、元の Python オブジェクトの再構築が簡素化されます。
<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>
json.dumps() はバイト オブジェクトを返すため、このコードは Python 3 では機能しません。 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 中国語 Web サイトの他の関連記事を参照してください。