Python Enum
メンバーをJSONにシリアライズして、結果のJSONをPythonオブジェクトにデシリアライズできるようにするにはどうすればよいですか?
たとえば、次のコード:
from enum import Enum
import json
class Status(Enum):
success = 0
json.dumps(Status.success)
エラーになります:
TypeError: <Status.success: 0> is not JSON serializable
どうすればそれを回避できますか?
任意の_enum.Enum
_メンバーをJSONにエンコードし、それを同じenumメンバーとしてデコードする場合(enumメンバーのvalue
属性ではなく)、カスタムを記述することでそれを行うことができます- JSONEncoder
クラス、および_object_hook
_引数として json.load()
または json.loads()
:
_PUBLIC_ENUMS = {
'Status': Status,
# ...
}
class EnumEncoder(json.JSONEncoder):
def default(self, obj):
if type(obj) in PUBLIC_ENUMS.values():
return {"__enum__": str(obj)}
return json.JSONEncoder.default(self, obj)
def as_enum(d):
if "__enum__" in d:
name, member = d["__enum__"].split(".")
return getattr(PUBLIC_ENUMS[name], member)
else:
return d
_
_as_enum
_関数は、EnumEncoder
、またはそれと同じように動作する何かを使用してエンコードされたJSONに依存します。
_PUBLIC_ENUMS
_のメンバーへの制限は、悪意を持って作成されたテキストを使用して、たとえばコードを呼び出してプライベート情報(たとえば、アプリケーションで使用される秘密キー)を無関係なデータベースフィールドに保存するのを避けるために必要ですその後、公開される可能性があります( http://chat.stackoverflow.com/transcript/message/35999686#35999686 を参照)。
使用例:
_>>> data = {
... "action": "frobnicate",
... "status": Status.success
... }
>>> text = json.dumps(data, cls=EnumEncoder)
>>> text
'{"status": {"__enum__": "Status.success"}, "action": "frobnicate"}'
>>> json.loads(text, object_hook=as_enum)
{'status': <Status.success: 0>, 'action': 'frobnicate'}
_
正しい答えは、シリアル化されたバージョンをどうするかによって異なります。
Pythonへのシリアル化を解除する場合は、 ゼロの答え を参照してください。
シリアル化されたバージョンが別の言語に移行する場合、おそらく代わりにIntEnum
を使用する必要があります。これは、対応する整数として自動的にシリアル化されます。
from enum import IntEnum
import json
class Status(IntEnum):
success = 0
failure = 1
json.dumps(Status.success)
そして、これは返します:
'0'
これは古いことは知っていますが、これは人々の役に立つと思います。私はちょうどこの正確な問題を調べて、文字列列挙を使用している場合、str
のサブクラスとして列挙を宣言すると、ほとんどすべての状況でうまく機能することを発見しました。
import json
from enum import Enum
class LogLevel(str, Enum):
DEBUG = 'DEBUG'
INFO = 'INFO'
print(LogLevel.DEBUG)
print(json.dumps(LogLevel.DEBUG))
print(json.loads('"DEBUG"'))
print(LogLevel('DEBUG'))
出力されます:
LogLevel.DEBUG
"DEBUG"
DEBUG
LogLevel.DEBUG
ご覧のとおり、JSONをロードすると文字列DEBUG
が出力されますが、LogLevelオブジェクトに簡単にキャストできます。カスタムJSONEncoderを作成したくない場合に適したオプションです。
Zero Piraeusの回答が好きでしたが、Botoとして知られるAmazon Web Services(AWS)のAPIを操作するために、それをわずかに変更しました。
class EnumEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Enum):
return obj.name
return json.JSONEncoder.default(self, obj)
次に、このメソッドをデータモデルに追加しました。
def ToJson(self) -> str:
return json.dumps(self.__dict__, cls=EnumEncoder, indent=1, sort_keys=True)
これが誰かの助けになることを願っています。
Python 3.7では、json.dumps(enum_obj, default=str)
を使用できます
jsonpickle
を使用している場合、最も簡単な方法は次のようになります。
from enum import Enum
import jsonpickle
@jsonpickle.handlers.register(Enum, base=True)
class EnumHandler(jsonpickle.handlers.BaseHandler):
def flatten(self, obj, data):
return obj.value # Convert to json friendly format
if __== '__main__':
class Status(Enum):
success = 0
error = 1
class SimpleClass:
pass
simple_class = SimpleClass()
simple_class.status = Status.success
json = jsonpickle.encode(simple_class, unpicklable=False)
print(json)
Jsonのシリアル化の後、予想どおり{"status": 0}
の代わりに
{"status": {"__objclass__": {"py/type": "__main__.Status"}, "_name_": "success", "_value_": 0}}