ホームページ  >  記事  >  バックエンド開発  >  「json.JSONEncoder」をサブクラス化せずにカスタム オブジェクトを JSON シリアル化可能にするにはどうすればよいですか?

「json.JSONEncoder」をサブクラス化せずにカスタム オブジェクトを JSON シリアル化可能にするにはどうすればよいですか?

Barbara Streisand
Barbara Streisandオリジナル
2024-10-30 20:25:30284ブラウズ

How can I make custom objects JSON serializable without subclassing `json.JSONEncoder`?

通常のエンコーダーを使用してオブジェクトを JSON シリアル化可能にする

シリアル化できないカスタム オブジェクトを 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 サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。