Как я должен JSON-сериализовать дочерние элементы перечисления?

#python #json #python-3.x #enums

Вопрос:

Если я напишу

 import json

class Color():
    FUSCHIA = 0x00
    TURQUOISE = 0x01
    EMERALD = 0x02


def my_default_serializer(o):
    return o.__dict__


def get_colors():
    return json.dumps(
        {
            'dark_bg': True,
            'colors_batch': [Color.TURQUOISE,
                             Color.EMERALD]
        },
        default=my_default_serializer
    )

print(get_colors())
 

тогда я получу, как и ожидалось:

 {"dark_bg": true, "colors_batch": [1, 2]}
 

Теперь предположим, что я модернизировал этот код, введя enum.Enum . Помимо прочего, это удобно, потому Enum что будет анализировать строку (например, считывать из файла), и поэтому я заменяю приведенный выше код на:

 import json
from enum import Enum

class Color(Enum):
    FUSCHIA = 0x00
    TURQUOISE = 0x01
    EMERALD = 0x02


def my_default_serializer(o):
    return o.__dict__


def get_colors():
    return json.dumps(
        {
            'dark_bg': True,
            'colors_batch': [Color['TURQUOISE'],
                             Color['EMERALD']]
        },
        default=my_default_serializer
    )

print(get_colors())
 

Это приводит к ошибке:

 AttributeError: 'mappingproxy' object has no attribute '__dict__'
 

(и если я не укажу сериализатор по умолчанию, я получу TypeError: Object of type 'Color' is not JSON serializable ).

Как я должен JSON-сериализовать дочерние элементы перечисления?

Комментарии:

1. можете ли вы поделиться определением big_obj ? почему my_default_serializer используется?

2. @balderman Спасибо, что спросил. Теперь я заменил вопрос на идентичный с автономным примером, иллюстрирующим проблему.

3. Мне удалось сериализовать Color . Смотреть ниже

Ответ №1:

смотрите ниже — кажется, работает. Идея состоит в том , чтобы переопределить default метод of JSONEncoder , проверить, является ли входящий аргумент экземпляром a Color , и обработать его.

 import json
from enum import Enum


class Color(Enum):
    FUSCHIA = 0x00
    TURQUOISE = 0x01
    EMERALD = 0x02


class EnumEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Enum):
            return {"__enum__": str(obj)}
        return json.JSONEncoder.default(self, obj)


def get_colors():
    return json.dumps(
        {
            'dark_bg': True,
            'colors_batch': [Color['TURQUOISE'],
                             Color['EMERALD']]
        }, cls=EnumEncoder
    )


print(get_colors())
 

Комментарии:

1. Ваш код работает только для перечисления Color -можете ли вы заставить его работать для любого перечисления?

2. @EthanFurman — Добавлена любая поддержка перечисления.

3. Вот и все. Только один комментарий: теперь мы получаем {"dark_bg": true, "colors_batch": [{"__enum__": "Color.TURQUOISE"}, {"__enum__": "Color.EMERALD"}]} вместо {"dark_bg": true, "colors_batch": [1, 2]} . Чтобы сохранить вывод без изменений (чтобы нам не пришлось изменять «другую сторону», замените return {"__enum__": str(obj)} на return obj.value .