Encoding Python Enum to JSON
You can't use anything but strings as keys in dictionaries you want to convert to JSON. The encoder doesn't give you any other options; the default
hook is only called for values of unknown type, never for keys.
Convert your keys to strings up front:
def convert_keys(obj, convert=str): if isinstance(obj, list): return [convert_keys(i, convert) for i in obj] if not isinstance(obj, dict): return obj return {convert(k): convert_keys(v, convert) for k, v in obj.items()}json.dumps(convert_keys(test))
This recursively handles your dictionary keys. Note that I included a hook; you can then choose how to convert enumeration values to strings:
def enum_names(key): if isinstance(key, TestEnum): return key.name return str(key)json.dumps(convert_keys(test, enum_names))
You can use the same function to reverse the process when loading from JSON:
def names_to_enum(key): try: return TestEnum[key] except KeyError: return keyconvert_keys(json.loads(json_data), names_to_enum)
Demo:
>>> def enum_names(key):... if isinstance(key, TestEnum):... return key.name... return str(key)...>>> json_data = json.dumps(convert_keys(test, enum_names))>>> json_data'{"one": "This", "two": "should", "three": "work!"}'>>> def names_to_enum(key):... try:... return TestEnum[key]... except KeyError:... return key...>>> convert_keys(json.loads(json_data), names_to_enum){<TestEnum.one: 'first'>: 'This', <TestEnum.two: 'second'>: 'should', <TestEnum.three: 'third'>: 'work!'}
It is an old question. But no one gave this very simple answer.
You just need to subclass your Enum from str.
import jsonfrom enum import Enumclass TestEnum(str, Enum): one = "first" two = "second" three = "third"test = {TestEnum.one : "This", TestEnum.two : "should", TestEnum.three : "work!"}print(json.dumps(test))
outputs:
{"first": "This", "second": "should", "third": "work!"}
I never use the builtin python enum anymore, I use a metaclass called "TypedEnum".
The reason is that the metaclass allows my string enums to act just like strings : they can be passed to functions that take strings, they can be serialized as strings (just like you want... right in a JSON encoding), but they are still a strong type (isA Enum) too.
https://gist.github.com/earonesty/81e6c29fa4c54e9b67d9979ddbd8489d
The number of weird bugs I've run into with regular enums is uncountable.
class TypedEnum(type): """This metaclass creates an enumeration that preserve isinstance(element, type).""" def __new__(mcs, cls, _bases, classdict): """Discover the enum members by removing all intrinsics and specials.""" object_attrs = set(dir(type(cls, (object,), {}))) member_names = set(classdict.keys()) - object_attrs member_names = member_names - set(name for name in member_names if name.startswith('_') and name.endswith('_')) new_class = None base = None for attr in member_names: value = classdict[attr] if new_class is None: # base class for all members is the type of the value base = type(classdict[attr]) new_class = super().__new__(mcs, cls, (base, ), classdict) setattr(new_class, "__member_names__", member_names) else: if not base == type(classdict[attr]): # noqa raise SyntaxError("Cannot mix types in TypedEnum") setattr(new_class, attr, new_class(value)) return new_class def __call__(cls, arg): for name in cls.__member_names__: if arg == getattr(cls, name): return type.__call__(cls, arg) raise ValueError("Invalid value '%s' for %s" % (arg, cls.__name__)) def __iter__(cls): """List all enum values.""" return (getattr(cls, name) for name in cls.__member_names__) def __len__(cls): """Get number of enum values.""" return len(cls.__member_names__)