[python] Enum 멤버를 JSON으로 직렬화

Enum결과 JSON을 다시 Python 객체로 역 직렬화 할 수 있도록 Python 멤버를 JSON으로 직렬화하려면 어떻게해야 합니까?

예를 들어 다음 코드는 다음과 같습니다.

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 으로 인코딩 한 다음 동일한 열거 형 멤버로 디코딩 하려는 경우 (단순히 열거 형 멤버의 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함수는를 사용하여 인코딩 된 JSON EnumEncoder또는 이와 동일하게 작동하는 무언가 에 의존 합니다.

의 구성원에 대한 제한 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'}


답변

나는 이것이 오래되었다는 것을 알고 있지만 이것이 사람들을 도울 것이라고 생각합니다. 나는 방금이 정확한 문제를 겪었고 문자열 열거 형을 사용하고 있는지 발견하여 열거 형 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를 생성하지 않으려는 경우 좋은 옵션입니다.


답변

정답은 직렬화 된 버전으로 수행하려는 작업에 따라 다릅니다.

Python으로 다시 직렬화 해제하려면 Zero ‘s answer를 참조하십시오 .

직렬화 된 버전이 다른 언어로 이동하는 경우 IntEnum대신 해당 정수로 자동 직렬화되는를 사용할 수 있습니다 .

from enum import IntEnum
import json

class Status(IntEnum):
    success = 0
    failure = 1

json.dumps(Status.success)

그리고 이것은 다음을 반환합니다.

'0'


답변

Python 3.7에서는
json.dumps(enum_obj, default=str)


답변

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)

누군가에게 도움이되기를 바랍니다.


답변

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 __name__ == '__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}}


답변

이것은 나를 위해 일했습니다.

class Status(Enum):
    success = 0

    def __json__(self):
        return self.value

다른 것을 변경할 필요가 없습니다. 분명히이 값에서 값을 얻을 수 있으며 나중에 직렬화 된 값을 다시 열거 형으로 변환하려면 다른 작업을 수행해야합니다.