1. ホーム
  2. python

[解決済み] セットをJSONシリアライズするには?

2022-04-21 06:47:35

質問

私はPythonの set を持つオブジェクトを含む __hash____eq__ メソッドを使用して、コレクションに重複が含まれないようにします。

この結果をjsonエンコードする必要があります set を渡しますが、空の setjson.dumps メソッドは TypeError .

  File "/usr/lib/python2.7/json/encoder.py", line 201, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib/python2.7/json/encoder.py", line 264, in iterencode
    return _iterencode(o, 0)
  File "/usr/lib/python2.7/json/encoder.py", line 178, in default
    raise TypeError(repr(o) + " is not JSON serializable")
TypeError: set([]) is not JSON serializable

の拡張を作成できることは知っています。 json.JSONEncoder クラスで、カスタム default メソッドに変換したいのですが、どこから手をつければいいのかさえわかりません。 set . の辞書を作成する必要があります。 set の値をデフォルトのメソッド内で使用し、そのエンコーディングを返すのでしょうか? 理想を言えば、デフォルトメソッドで、元のエンコーダーで詰まってしまうすべてのデータ型を扱えるようにしたいです(データソースとしてMongoを使っているので、日付でもこのエラーが発生するようです)。

正しい方向へのヒントがあれば、ありがたいです。

EDITです。

回答ありがとうございました もっと正確に伝えるべきだったかもしれません。

の制限を回避するために、ここの回答を活用(upvoted)しました。 set しかし、内部キーも問題です。

のオブジェクトは set に変換される複合オブジェクトです。 __dict__ しかし、それ自身は、jsonエンコーダーの基本型に不適格な値をプロパティに含むことができます。

この中には、さまざまな型が入っています。 set ハッシュは基本的にエンティティの一意な ID を計算しますが、NoSQL の真の精神として、子オブジェクトが何を含むかは正確にはわかりません。

あるオブジェクトは、日付の値を含む starts 一方、別のスキーマでは、quot;非プリミティブオブジェクトを含むキーが存在しないかもしれません。

そのため、私が思いついた唯一の解決策は JSONEncoder を置き換えるために default メソッドで異なるケースをオンにすることができます。しかし、この方法がよくわからないし、ドキュメントもあいまいです。 ネストされたオブジェクトの中で default それとも、単にオブジェクト全体を見る一般的なインクルード/ディスカードなのでしょうか? その方法は、ネストされた値にどのように対応するのでしょうか? 以前の質問に目を通しましたが、大文字小文字を区別するエンコーディング(残念ながらここで必要なことのように思えます)に対する最良のアプローチを見つけることができないようです。

どのように解決するのですか?

JSON そのため、JSONでシリアライズする場合は、これらのデータ型のいずれかとして表現する必要があります。

で示したように jsonモジュールのドキュメント によって、この変換は自動的に行われます。 JSONEncoder JSONDecoder を使用してセットを辞書に変換する場合は、必要な他の構造をあきらめることになります(セットをリストに変換すると、通常のリストを復元する機能が失われます。 dict.fromkeys(s) となると、辞書を復元する機能は失われます)。

より洗練されたソリューションとしては、他のネイティブJSONタイプと共存できるカスタムタイプを構築することです。 これにより、リスト、セット、ディクショナリ、小数、datetimeオブジェクトなどを含むネストされた構造を保存できます。

from json import dumps, loads, JSONEncoder, JSONDecoder
import pickle

class PythonObjectEncoder(JSONEncoder):
    def default(self, obj):
        try:
            return {'_python_object': pickle.dumps(obj).decode('latin-1')}
        except pickle.PickleError:
            return super().default(obj)

def as_python_object(dct):
    if '_python_object' in dct:
        return pickle.loads(dct['_python_object'].encode('latin-1'))
    return dct

以下は、リスト、ディクテ、セットを扱えることを示すサンプルセッションです。

>>> data = [1,2,3, set(['knights', 'who', 'say', 'ni']), {'key':'value'}, Decimal('3.14')]

>>> j = dumps(data, cls=PythonObjectEncoder)

>>> loads(j, object_hook=as_python_object)
[1, 2, 3, set(['knights', 'say', 'who', 'ni']), {'key': 'value'}, Decimal('3.14')]

あるいは、以下のような汎用的な直列化技術を使用することも有効です。 YAML , ツイストゼリー またはPythonの pickleモジュール . これらはそれぞれ、より多くの種類のデータ型をサポートしています。