クラスJSONをシリアライズ可能にする方法


834

Pythonクラスをシリアライズ可能にする方法は?

単純なクラス:

class FileItem:
    def __init__(self, fname):
        self.fname = fname

次の出力を取得できるようにするにはどうすればよいですか。

>>> import json

>>> my_file = FileItem('/foo/bar')
>>> json.dumps(my_file)
TypeError: Object of type 'FileItem' is not JSON serializable

エラーなし


31
答えがすべて「クラスをシリアル化するにはどうすればよいですか?」という質問に答えるように見えるのは残念です。アクションの質問ではなく、「クラスをシリアライズ可能にするにはどうすればよいですか?」これらの回答では、オブジェクトをシリアル化する他のモジュールにオブジェクトを渡すのではなく、自分でシリアル化を行っていると想定しています。
カイルデラニー

Python3.5以降を使用している場合は、jsonを使用できます。オブジェクト(およびそのすべての属性を再帰的に)をdictに変換します。 import jsons以下の回答を参照してください-完全に正常に機能します
tswaehn

回答:


551

予想される出力について何か考えがありますか?たとえば、これで十分でしょうか?

>>> f  = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'

その場合は、単にを呼び出すことができますjson.dumps(f.__dict__)

さらにカスタマイズされた出力が必要な場合はJSONEncoder、独自のカスタムシリアライゼーションをサブクラス化して実装する必要があります。

簡単な例については、以下を参照してください。

>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
        def default(self, o):
            return o.__dict__    

>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'

次に、このクラスをkwarg json.dumps()としてメソッドにcls渡します。

json.dumps(cls=MyEncoder)

デコードしたい場合はobject_hookJSONDecoderクラスにカスタムを提供する必要があります。例えば

>>> def from_json(json_object):
        if 'fname' in json_object:
            return FileItem(json_object['fname'])
>>> f = JSONDecoder(object_hook = from_json).decode('{"fname": "/foo/bar"}')
>>> f
<__main__.FileItem object at 0x9337fac>
>>> 

44
を使用__dict__してもすべての場合で機能するわけではありません。オブジェクトがインスタンス化された後に属性が設定されていない場合、__dict__完全に設定されていない可能性があります。上記の例では問題ありませんが、エンコードするクラス属性がある場合、それらが__dict__クラスの__init__呼び出しで変更されていない限り、またはオブジェクトがインスタンス化された後に他の方法で変更されていない限り、それらはリストされません。
Kris Hardy

8
+1。ただし、from_json()オブジェクトフックとして使用される関数にはelse: return json_objectステートメントが必要です。そのため、一般的なオブジェクトも処理できます。
jogojapan 2013年

8
@KrisHardy __dict____slots__、新しいスタイルクラスで使用する場合にも機能しません。
badp

7
JSONEncoder上記のカスタムを使用して、__json_serializable__メソッドの存在を確認し、それを呼び出してオブジェクトのJSONシリアル化可能な表現を取得するなど、カスタムプロトコルを作成できます。これは次のように、他のPythonのパターンに合わせてだろう__getitem____str____eq__、と__len__
jpmc26 2015

5
__dict__また、オブジェクトの属性が別のオブジェクトである場合など、再帰的に機能しません。
ニール

634

簡単な機能の簡単な解決策を次に示します。

.toJSON() 方法

JSONシリアライズ可能クラスの代わりに、シリアライザーメソッドを実装します。

import json

class Object:
    def toJSON(self):
        return json.dumps(self, default=lambda o: o.__dict__, 
            sort_keys=True, indent=4)

したがって、シリアル化するためにそれを呼び出すだけです:

me = Object()
me.name = "Onur"
me.age = 35
me.dog = Object()
me.dog.name = "Apollo"

print(me.toJSON())

出力されます:

{
    "age": 35,
    "dog": {
        "name": "Apollo"
    },
    "name": "Onur"
}

82
非常に限られています。dict {"foo": "bar"、 "baz": "bat"}がある場合、JSONに簡単にシリアル化されます。代わりに{"foo": "bar"、 "baz":MyObject()}がある場合、それはできません。ネストされたオブジェクトが明示的にではなく再帰的にJSONにシリアル化されるのが理想的な状況です。
Mark E. Haase 2013

30
それでも動作します。あなたがいませんo.__dict___。独自の例を試してください: class MyObject(): def __init__(self): self.prop = 1 j = json.dumps({ "foo": "bar", "baz": MyObject() }, default=lambda o: o.__dict__)
OnurYıldırım2013

14
このソリューションは可逆的ですか?つまり、jsonからオブジェクトを再構築するのは簡単ですか?
ホルヘレイタオ、2015

2
@JCLeitãoいいえ。同じフィールドを持つ2つの異なるクラスを使用できます。そのクラスのオブジェクトaとb(おそらく同じプロパティを持つ)は同じa.__dict__/ を持ちb.__dict__ます。
Martin Thoma

7
これはdatetime.datetimeインスタンスでは機能しません。これは、次のエラーがスローされます:'datetime.datetime' object has no attribute '__dict__'
ブルーノ・フィンガー

171

より複雑なクラスでは、jsonpickleツールを検討できます。

jsonpickleは、JSONとの間の複雑なPythonオブジェクトのシリアライズとデシリアライズのためのPythonライブラリです。

stdlibのjson、simplejson、demjsonなど、PythonをJSONにエンコードするための標準Pythonライブラリは、直接JSONに対応するPythonプリミティブ(dicts、lists、strings、intなど)のみを処理できます。jsonpickleはこれらのライブラリの上に構築され、より複雑なデータ構造をJSONにシリアル化できるようにします。jsonpickleは高度に構成可能で拡張可能であり、ユーザーはJSONバックエンドを選択して追加のバックエンドを追加できます。

(PyPiのjsonpickleへのリンク)


32
C#から来た、これは私が期待していたものです。シンプルなワンライナーで、クラスをいじる必要はありません。
Jerther、2015

2
jsonpickleは素晴らしいです。それは、クラスの多くのレベルを持つ巨大な、複雑な、厄介なオブジェクトのために完璧に働いた
wisbucky

これをファイルに保存する適切な方法の例はありますか?ドキュメントは、jsonpickleオブジェクトをエンコードおよびデコードする方法のみを示しています。また、これはパンダのデータフレームを含むdictsのdictをデコードすることができませんでした。
user5359531

3
@ user5359531あなたが使用することができますobj = jsonpickle.decode(file.read())file.write(jsonpickle.encode(obj))
Kilian Batzner 2017年

1
特にdjangoに関する質問:セッションデータのシリアル化にjsonpickleを使用すると、pickleと同じ脆弱性がありますか?(ここに記載されているdocs.djangoproject.com/en/1.11/topics/http/sessions/…)?
ポールボルマンス2017年

89

答えのほとんどはjson.dumps()への呼び出しを変更することを含みますが、これは常に可能または望ましいことではありません(たとえば、フレームワークコンポーネント内で発生する可能性があります)。

json.dumps(obj)をそのまま呼び出すことができるようにしたい場合 、簡単な解決策はdictから継承することです:

class FileItem(dict):
    def __init__(self, fname):
        dict.__init__(self, fname=fname)

f = FileItem('tasks.txt')
json.dumps(f)  #No need to change anything here

これは、クラスが単なる基本的なデータ表現である場合に機能します。トリッキーなことに、常にキーを明示的に設定できます。


2
これは本当に素晴らしい解決策になる可能性があります:)私は私の場合はそうだと信じています。利点:オブジェクトの「形状」をinitのあるクラスにすることで伝達します。本質的にシリアル化可能で、reprとして解釈できるように見えます。
PascalVKooten 2016

1
「ドットアクセス」はまだ欠落していますが:(
PascalVKooten

2
ああそうだ!ありがとう、なぜこれが受け入れられない答えであるかはわかりません。を変更することdumpsは良い解決策ではないことに完全に同意します。ちなみに、ほとんどの場合、dict継承と委譲を併用する必要があります。つまりdict、クラス内に型属性があり、初期化としてこの属性をパラメーターとして渡しますsuper().__init__(self.elements)
cglacet

47

私はオヌールの答えが好きですがtoJSON()、オブジェクトを自分自身をシリアル化するオプションのメソッドを含めるように拡張します:

def dumper(obj):
    try:
        return obj.toJSON()
    except:
        return obj.__dict__
print json.dumps(some_big_object, default=dumper, indent=2)

これが、既存のjson.dumps処理を使用することとカスタム処理を導入することの間の最良のバランスであることがわかりました。ありがとう!
ダニエル・バックマスター、2015

12
私は実際にこれが本当に好きです。しかし、try-catchおそらくif 'toJSON' in obj.__attrs__():...サイレントエラー(toJSON()でエラーが発生しない場合以外の理由でエラーが発生した場合)を避けるために...データの破損につながる可能性のあるエラー。
スクラーク

39

別のオプションは、JSONダンプを独自のクラスでラップすることです。

import json

class FileItem:
    def __init__(self, fname):
        self.fname = fname

    def __repr__(self):
        return json.dumps(self.__dict__)

または、さらに良いことに、クラスからFileItemクラスをサブクラス化しますJsonSerializable

import json

class JsonSerializable(object):
    def toJson(self):
        return json.dumps(self.__dict__)

    def __repr__(self):
        return self.toJson()


class FileItem(JsonSerializable):
    def __init__(self, fname):
        self.fname = fname

テスト:

>>> f = FileItem('/foo/bar')
>>> f.toJson()
'{"fname": "/foo/bar"}'
>>> f
'{"fname": "/foo/bar"}'
>>> str(f) # string coercion
'{"fname": "/foo/bar"}'

2
こんにちは、私はこの「カスタムエンコーダー」アプローチがあまり好きではありません。クラスjsonをシリアライズ可能にできれば、より良いでしょう。私は試して、試してみて、何もしません。これを行う方法についてのアイデアはありますか?問題は、jsonモジュールが組み込みのPythonタイプに対してクラスをテストし、さらにカスタムクラスについてはエンコーダーを作成することです:)。偽造できますか?だから私は私のクラスに何かをすることができますので、それはjsonモジュールへの単純なリストのように振る舞います 私はsubclasscheckinstancecheckを試しますが、何もしません。
Bojan Radojevic 2012

@ADRENALINすべてのクラス属性値がシリアル化可能であり、ハッキングを気にしない場合は、プライマリタイプ(おそらくdict)から継承できます。また、標準のエンコーダーの代わりにjsonpickleやjson_tricksなどを使用することもできます(まだカスタムエンコーダーですが、作成または呼び出す必要のあるエンコーダーはありません)。前者はインスタンスをpickle 化し、後者は__json__encode__/ を実装することで変更できる属性のdictとしてそれを格納します__json_decode__(開示:私は最後のものを作成しました)。
マーク

30

次のto_jsonようにクラスにメソッドを追加するだけです:

def to_json(self):
  return self.message # or how you want it to be serialized

そして、このコードをこの答えから)、すべての一番上のどこかに追加してください:

from json import JSONEncoder

def _default(self, obj):
    return getattr(obj.__class__, "to_json", _default.default)(obj)

_default.default = JSONEncoder().default
JSONEncoder.default = _default

JSONEncoder.default()が自動的に特別な「to_json()」メソッドをチェックし、見つかった場合はそれを使用してオブジェクトをエンコードするため、これはインポート時にjsonモジュールをサルパッチします。

Onurが言ったように、今度はjson.dumps()プロジェクトのすべてを更新する必要はありません。


6
本当にありがとう!これは、私がやりたいことを実行できる唯一の答えです。既存のコードを変更せずにオブジェクトをシリアル化できることです。他の方法はほとんど私にとってはうまくいきません。オブジェクトはサードパーティライブラリで定義されており、シリアル化コードもサードパーティです。それらを変更するのは面倒です。あなたの方法では、私はするだけですTheObject.to_json = my_serializer
Yongwei Wu

24

先日この問題に遭遇し、ネストされたオブジェクト継承されたフィールドを処理できるPythonオブジェクトのエンコーダのより一般的なバージョンを実装しました:

import json
import inspect

class ObjectEncoder(json.JSONEncoder):
    def default(self, obj):
        if hasattr(obj, "to_json"):
            return self.default(obj.to_json())
        elif hasattr(obj, "__dict__"):
            d = dict(
                (key, value)
                for key, value in inspect.getmembers(obj)
                if not key.startswith("__")
                and not inspect.isabstract(value)
                and not inspect.isbuiltin(value)
                and not inspect.isfunction(value)
                and not inspect.isgenerator(value)
                and not inspect.isgeneratorfunction(value)
                and not inspect.ismethod(value)
                and not inspect.ismethoddescriptor(value)
                and not inspect.isroutine(value)
            )
            return self.default(d)
        return obj

例:

class C(object):
    c = "NO"
    def to_json(self):
        return {"c": "YES"}

class B(object):
    b = "B"
    i = "I"
    def __init__(self, y):
        self.y = y

    def f(self):
        print "f"

class A(B):
    a = "A"
    def __init__(self):
        self.b = [{"ab": B("y")}]
        self.c = C()

print json.dumps(A(), cls=ObjectEncoder, indent=2, sort_keys=True)

結果:

{
  "a": "A", 
  "b": [
    {
      "ab": {
        "b": "B", 
        "i": "I", 
        "y": "y"
      }
    }
  ], 
  "c": {
    "c": "YES"
  }, 
  "i": "I"
}

1
これは少し古いですが、循環インポートエラーが発生しています。だからreturn obj最後の行の代わりに私はこれをやったreturn super(ObjectEncoder, self).default(obj)。参考HERE
SomeTypeFoo

24

Python3.5以降を使用してjsonsいる場合は、を使用できます。オブジェクト(およびそのすべての属性を再帰的に)をdictに変換します。

import jsons

a_dict = jsons.dump(your_object)

または、文字列が必要な場合:

a_str = jsons.dumps(your_object)

または、クラスが実装されている場合jsons.JsonSerializable

a_dict = your_object.json

3
Python 3.7以降を使用できる場合、PythonクラスをdictsとJSON文字列に(およびその逆に)変換する最もクリーンな解決策は、jsonsライブラリをdataclassesと混合することです。これまでのところ、私にとってとても良いです!
Ruluk、

3
これは外部ライブラリであり、標準のPythonインストールに組み込まれていません。
Noumenon

スロット属性を持つクラスのみ
yehudahs

できますが、スロットを使用する必要はありません。特定のクラスのシグネチャに従ってダンプする場合のみ、スロットが必要になります。次のバージョン1.1.0では、これも当てはまりません。
RH

11
import simplejson

class User(object):
    def __init__(self, name, mail):
        self.name = name
        self.mail = mail

    def _asdict(self):
        return self.__dict__

print(simplejson.dumps(User('alice', 'alice@mail.com')))

標準を使用する場合json、uはdefault関数を定義する必要があります

import json
def default(o):
    return o._asdict()

print(json.dumps(User('alice', 'alice@mail.com'), default=default))

2
ラムダを使用して_asdict関数を削除することで、これを単純化しました json.dumps(User('alice', 'alice@mail.com'), default=lambda x: x.__dict__)
JustEngland

8

json印刷できるオブジェクトの点で制限されており、jsonpickle(必要な場合がありますpip install jsonpickle)テキストをインデントできない点で制限されています。クラスを変更できないオブジェクトの内容を調べたい場合でも、次の方法よりも簡単な方法はありません。

 import json
 import jsonpickle
 ...
 print  json.dumps(json.loads(jsonpickle.encode(object)), indent=2)

注:それでも、オブジェクトメソッドを出力することはできません。


6

このクラスはトリックを実行でき、オブジェクトを標準のjsonに変換します。

import json


class Serializer(object):
    @staticmethod
    def serialize(object):
        return json.dumps(object, default=lambda o: o.__dict__.values()[0])

使用法:

Serializer.serialize(my_object)

で働くpython2.7python3


私はこの方法が一番好きでした。メンバー/メソッドがシリアル化可能ではない、より複雑なオブジェクトをシリアル化しようとすると問題が発生しました。これは、より多くのオブジェクトで機能する私の実装です: `` `class Serializer(object):@staticmethod def serialize(obj):def check(o):for k、v in o .__ dict __。items():try:_ = json .dumps(v)o .__ dict __ [k] = v TypeErrorを除く:o .__ dict __ [k] = str(v)return o return json.dumps(check(obj).__ dict__、indent = 2) `` `
Willチャールトン

4
import json

class Foo(object):
    def __init__(self):
        self.bar = 'baz'
        self._qux = 'flub'

    def somemethod(self):
        pass

def default(instance):
    return {k: v
            for k, v in vars(instance).items()
            if not str(k).startswith('_')}

json_foo = json.dumps(Foo(), default=default)
assert '{"bar": "baz"}' == json_foo

print(json_foo)

ドキュメントから:パラメータdefault(obj)は、objのシリアル化可能なバージョンを返すか、TypeErrorを発生させる関数です。デフォルトではdefault単純にTypeErrorが発生します。
ラッキードナルド2016年

4

jaracoはかなりきちんとした答えを出しました。私はいくつかのマイナーなものを修正する必要がありましたが、これはうまくいきます:

コード

# Your custom class
class MyCustom(object):
    def __json__(self):
        return {
            'a': self.a,
            'b': self.b,
            '__python__': 'mymodule.submodule:MyCustom.from_json',
        }

    to_json = __json__  # supported by simplejson

    @classmethod
    def from_json(cls, json):
        obj = cls()
        obj.a = json['a']
        obj.b = json['b']
        return obj

# Dumping and loading
import simplejson

obj = MyCustom()
obj.a = 3
obj.b = 4

json = simplejson.dumps(obj, for_json=True)

# Two-step loading
obj2_dict = simplejson.loads(json)
obj2 = MyCustom.from_json(obj2_dict)

# Make sure we have the correct thing
assert isinstance(obj2, MyCustom)
assert obj2.__dict__ == obj.__dict__

ロードには2つのステップが必要です。現時点では、この__python__プロパティは使用されていません。

これはどれくらい一般的ですか?

AlJohriの方法を使用して、アプローチの人気度を確認します。

シリアライゼーション(Python-> JSON):

逆シリアル化(JSON-> Python):


4

これは私にとってうまくいきました:

class JsonSerializable(object):

    def serialize(self):
        return json.dumps(self.__dict__)

    def __repr__(self):
        return self.serialize()

    @staticmethod
    def dumper(obj):
        if "serialize" in dir(obj):
            return obj.serialize()

        return obj.__dict__

その後

class FileItem(JsonSerializable):
    ...

そして

log.debug(json.dumps(<my object>, default=JsonSerializable.dumper, indent=2))

3

パッケージをインストールしてもかまわない場合は、json-tricksを使用できます。

pip install json-tricks

その後はあなただけインポートする必要があるdump(s)からjson_tricks代わりにJSONの、そしてそれは通常の仕事よ。

from json_tricks import dumps
json_str = dumps(cls_instance, indent=4)

与えるでしょう

{
        "__instance_type__": [
                "module_name.test_class",
                "MyTestCls"
        ],
        "attributes": {
                "attr": "val",
                "dct_attr": {
                        "hello": 42
                }
        }
}

そして、それは基本的にそれです!


これは一般的にうまくいきます。いくつかの例外があります。たとえば、特別なことがで発生した__new__場合や、メタクラスの魔法が進行中の場合などです。

明らかにロードも機能します(そうでなければポイントは何ですか):

from json_tricks import loads
json_str = loads(json_str)

これは、module_name.test_class.MyTestClsがインポート可能であり、互換性のない方法で変更されていないことを前提としています。辞書や何かではなくインスタンスが返されます。これは、ダンプしたものと同じコピーである必要があります。

何かがシリアライズ(デ)される方法をカスタマイズしたい場合は、次のようにクラスに特別なメソッドを追加できます。

class CustomEncodeCls:
        def __init__(self):
                self.relevant = 42
                self.irrelevant = 37

        def __json_encode__(self):
                # should return primitive, serializable types like dict, list, int, string, float...
                return {'relevant': self.relevant}

        def __json_decode__(self, **attrs):
                # should initialize all properties; note that __init__ is not called implicitly
                self.relevant = attrs['relevant']
                self.irrelevant = 12

例として、属性パラメーターの一部のみをシリアル化します。

また、無料のボーナスとして、numpy配列(日付と時刻)、順序付けられたマップの(逆)シリアル化、およびjsonにコメントを含める機能が提供されます。

免責事項:私はあなたと同じ問題を抱えていたので、json_tricksを作成しました。


1
私はjson_tricksをテストしたところ、(2019年に)美しく機能しました。
pauljohn32

2

jsonwebは私にとって最良のソリューションのようです。http://www.jsonweb.info/en/latest/を参照してください

from jsonweb.encode import to_object, dumper

@to_object()
class DataModel(object):
  def __init__(self, id, value):
   self.id = id
   self.value = value

>>> data = DataModel(5, "foo")
>>> dumper(data)
'{"__type__": "DataModel", "id": 5, "value": "foo"}'

ネストされたオブジェクトでうまく機能しますか?デコードとエンコードを含む
Simone Zandara

1


これが私の3セントです... これは、ツリーのようなpythonオブジェクトの明示的なjsonシリアル化を示しています。
注:このようなコードが実際に必要な場合は、ツイストしたFilePathクラスを使用できます。

import json, sys, os

class File:
    def __init__(self, path):
        self.path = path

    def isdir(self):
        return os.path.isdir(self.path)

    def isfile(self):
        return os.path.isfile(self.path)

    def children(self):        
        return [File(os.path.join(self.path, f)) 
                for f in os.listdir(self.path)]

    def getsize(self):        
        return os.path.getsize(self.path)

    def getModificationTime(self):
        return os.path.getmtime(self.path)

def _default(o):
    d = {}
    d['path'] = o.path
    d['isFile'] = o.isfile()
    d['isDir'] = o.isdir()
    d['mtime'] = int(o.getModificationTime())
    d['size'] = o.getsize() if o.isfile() else 0
    if o.isdir(): d['children'] = o.children()
    return d

folder = os.path.abspath('.')
json.dump(File(folder), sys.stdout, default=_default)

1

PeeweeのモデルをPostgreSQLに保存しようとしたときにこの問題に遭遇しましたJSONField

しばらく苦労した後、これが一般的な解決策です。

私のソリューションの鍵は、Pythonのソースコードを調べ、コードド​​キュメント(ここで説明)が既存のものjson.dumpsを拡張して他のデータ型をサポートする方法をすでに説明していることを理解することです。

現在、JSONにシリアル化できないいくつかのフィールドを含むモデルがあり、JSONフィールドを含むモデルが最初は次のようになっているとします。

class SomeClass(Model):
    json_field = JSONField()

JSONEncoderこのようなカスタムを定義するだけです:

class CustomJsonEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, SomeTypeUnsupportedByJsonDumps):
            return < whatever value you want >
        return json.JSONEncoder.default(self, obj)

    @staticmethod
    def json_dumper(obj):
        return json.dumps(obj, cls=CustomJsonEncoder)

そして、それをJSONField以下のように使用してください:

class SomeClass(Model):
    json_field = JSONField(dumps=CustomJsonEncoder.json_dumper)

キーはdefault(self, obj)上記の方法です。... is not JSON serializablePythonから受け取ったすべての苦情に対して、unserializable-to-JSONタイプ(Enumまたはなどdatetime)を処理するコードを追加するだけです

たとえば、次のクラスから継承するクラスをサポートする方法を示しますEnum

class TransactionType(Enum):
   CURRENT = 1
   STACKED = 2

   def default(self, obj):
       if isinstance(obj, TransactionType):
           return obj.value
       return json.JSONEncoder.default(self, obj)

最後に、上記のように実装されたコードを使用すると、Peeweeモデルを次のようなJSONシリアライズ可能なオブジェクトに変換できます。

peewee_model = WhateverPeeweeModel()
new_model = SomeClass()
new_model.json_field = model_to_dict(peewee_model)

上記のコードはPeeweeに(ある程度)固有のものでしたが、私は次のように考えています。

  1. 一般的に他のORM(Djangoなど)に適用できます
  2. また、どのようにjson.dumps機能するかを理解している場合、このソリューションは一般にPython(sans ORM)でも機能します

質問があれば、コメント欄に投稿してください。ありがとう!


1

この関数は、再帰を使用してディクショナリのすべての部分を反復処理し、組み込み型ではないクラスのrepr()メソッドを呼び出します。

def sterilize(obj):
    object_type = type(obj)
    if isinstance(obj, dict):
        return {k: sterilize(v) for k, v in obj.items()}
    elif object_type in (list, tuple):
        return [sterilize(v) for v in obj]
    elif object_type in (str, int, bool):
        return obj
    else:
        return obj.__repr__()


0

私は自分の解決策を思いつきました。このメソッドを使用して、シリアル化するドキュメント(dictlistObjectIdなど)を渡します。

def getSerializable(doc):
    # check if it's a list
    if isinstance(doc, list):
        for i, val in enumerate(doc):
            doc[i] = getSerializable(doc[i])
        return doc

    # check if it's a dict
    if isinstance(doc, dict):
        for key in doc.keys():
            doc[key] = getSerializable(doc[key])
        return doc

    # Process ObjectId
    if isinstance(doc, ObjectId):
        doc = str(doc)
        return doc

    # Use any other custom serializting stuff here...

    # For the rest of stuff
    return doc

0

日時オブジェクトのシリアル化の問題を解決するために、デコレーターを使用することにしました。これが私のコードです:

#myjson.py
#Author: jmooremcc 7/16/2017

import json
from datetime import datetime, date, time, timedelta
"""
This module uses decorators to serialize date objects using json
The filename is myjson.py
In another module you simply add the following import statement:
    from myjson import json

json.dumps and json.dump will then correctly serialize datetime and date 
objects
"""

def json_serial(obj):
    """JSON serializer for objects not serializable by default json code"""

    if isinstance(obj, (datetime, date)):
        serial = str(obj)
        return serial
    raise TypeError ("Type %s not serializable" % type(obj))


def FixDumps(fn):
    def hook(obj):
        return fn(obj, default=json_serial)

    return hook

def FixDump(fn):
    def hook(obj, fp):
        return fn(obj,fp, default=json_serial)

    return hook


json.dumps=FixDumps(json.dumps)
json.dump=FixDump(json.dump)


if __name__=="__main__":
    today=datetime.now()
    data={'atime':today, 'greet':'Hello'}
    str=json.dumps(data)
    print str

上記のモジュールをインポートすることにより、他のモジュールは通常の方法で(デフォルトのキーワードを指定せずに)jsonを使用して、日時オブジェクトを含むデータをシリアル化します。日時シリアライザコードは、json.dumpsおよびjson.dumpに対して自動的に呼び出されます。


0

ロストコーダーの方法が一番好きでした。メンバー/メソッドがシリアル化可能でないより複雑なオブジェクトをシリアル化しようとすると、問題が発生しました。これが、より多くのオブジェクトで機能する私の実装です。

class Serializer(object):
    @staticmethod
    def serialize(obj):
        def check(o):
            for k, v in o.__dict__.items():
                try:
                    _ = json.dumps(v)
                    o.__dict__[k] = v
                except TypeError:
                    o.__dict__[k] = str(v)
            return o
        return json.dumps(check(obj).__dict__, indent=2)

0

パッケージをインストールできる場合は、私のプロジェクトで問題なく機能するdillを試すことをお勧めします。このパッケージの良い点は、と同じインターフェースpickleを備えていることです。そのためpickle、プロジェクトですでに使用している場合は、単にdill場合は、コードを変更することなく、スクリプトを実行して、確認できます。だから、試してみるのは非常に安価なソリューションです!

(完全な開示禁止:私は、dillプロジェクトとはまったく関係がなく、これまでに貢献したことがありません。)

パッケージをインストールします。

pip install dill

次にdillpickle次の代わりにインポートするコードを編集します。

# import pickle
import dill as pickle

スクリプトを実行して、機能するかどうかを確認します。(もしそうなら、コードをクリーンアップして、もはやpickleモジュール名を!)

プロジェクトページdillからシリアル化できるデータ型とシリアル化できないデータ型の詳細:

dill 次の標準タイプをピクルすることができます。

none、type、bool、int、long、float、complex、str、unicode、tuple、list、dict、file、buffer、builtin、新旧両方のスタイルクラス、新旧のスタイルクラスのインスタンス、set、frozenset、array 、関数、例外

dill より「エキゾチック」な標準タイプをピクルすることもできます:

利回りを持つ関数、ネストされた関数、ラムダ、セル、メソッド、unboundmethod、モジュール、コード、methodwrapper、dictproxy、methoddescriptor、getsetdescriptor、memberdescriptor、wrapperdescriptor、xrange、slice、notimplemented、省略記号、quit

dill これらの標準タイプはまだピクルできません:

フレーム、ジェネレータ、トレースバック



0

別のオプションを追加するには:attrsパッケージとasdictメソッドを使用できます。

class ObjectEncoder(JSONEncoder):
    def default(self, o):
        return attr.asdict(o)

json.dumps(objects, cls=ObjectEncoder)

そして元に戻す

def from_json(o):
    if '_obj_name' in o:
        type_ = o['_obj_name']
        del o['_obj_name']
        return globals()[type_](**o)
    else:
        return o

data = JSONDecoder(object_hook=from_json).decode(data)

クラスはこのようになります

@attr.s
class Foo(object):
    x = attr.ib()
    _obj_name = attr.ib(init=False, default='Foo')

0

オヌールの答えに加えて、あなたはおそらく以下のような日時タイプを扱いたいと思うでしょう。
(処理するために: 'datetime.datetime'オブジェクトには属性 ' dict '例外がありません。)

def datetime_option(value):
    if isinstance(value, datetime.date):
        return value.timestamp()
    else:
        return value.__dict__

使用法:

def toJSON(self):
    return json.dumps(self, default=datetime_option, sort_keys=True, indent=4)

0

まず、オブジェクトをJSON準拠にする必要があります。これにより、標準のJSONモジュールを使用してオブジェクトをダンプできます。私はこのようにしました:

def serialize(o):
    if isinstance(o, dict):
        return {k:serialize(v) for k,v in o.items()}
    if isinstance(o, list):
        return [serialize(e) for e in o]
    if isinstance(o, bytes):
        return o.decode("utf-8")
    return o

0

Quinten Cabo答え基づく

def sterilize(obj):
    if type(obj) in (str, float, int, bool, type(None)):
        return obj
    elif isinstance(obj, dict):
        return {k: sterilize(v) for k, v in obj.items()}
    elif hasattr(obj, '__iter__') and callable(obj.__iter__):
        return [sterilize(v) for v in obj]
    elif hasattr(obj, '__dict__'):
        return {k: sterilize(v) for k, v in obj.__dict__.items() if k not in ['__module__', '__dict__', '__weakref__', '__doc__']}
    else:
        return repr(obj)

違いは

  1. だけlistでなく、イテラブルで動作しますtupleで機能します(NumPy配列などで機能します)
  2. 動的な型( __dict__)で機能します。
  3. ネイティブ型が含まれていますfloatし、Noneそう、彼らは文字列に変換されません。

読者への課題として扱うの__slots__は、、反復可能でメンバーを持つクラス、辞書でありメンバーも持つクラスなどです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.