将Python枚举编码为JSON [英] Encoding Python Enum to JSON

查看:100
本文介绍了将Python枚举编码为JSON的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一本字典,其中一些键是Enum实例(enum.Enum的子类)。我正在尝试根据文档。我只想让输出的JSON中的键成为Enum名称的字符串。例如, {TestEnum.one:somevalue} 将被编码为 { one:somevalue}

I have a dictionary where some of the keys are Enum instances (subclasses of enum.Enum). I am attempting to encode the dictionary into a JSON string using a custom JSON Encoder class as per the documentation. All I want is to have the keys in the outputted JSON be the strings of the Enum names. For example { TestEnum.one : somevalue } would be encoded to { "one" : somevalue }.

我编写了一个简单的测试用例,如下所示,已在干净的virtualenv中对其进行了测试:

I have written a simple test case, shown below, which I have tested in a clean virtualenv:

import json

from enum import Enum

class TestEnum(Enum):
    one = "first"
    two = "second"
    three = "third"

class TestEncoder(json.JSONEncoder):
    """ Custom encoder class """

    def default(self, obj):

        print("Default method called!")

        if isinstance(obj, TestEnum):
            print("Seen TestEnum!")
            return obj.name

        return json.JSONEncoder.default(self, obj)

def encode_enum(obj):
    """ Custom encoder method """

    if isinstance(obj, TestEnum):
        return obj.name
    else:
        raise TypeError("Don't know how to decode this")

if __name__ == "__main__":

    test = {TestEnum.one : "This",
            TestEnum.two : "should",
            TestEnum.three : "work!"}

    # Test dumps with Encoder method
    #print("Test with encoder method:")
    #result = json.dumps(test, default=encode_enum)
    #print(result)

    # Test dumps with Encoder Class
    print("Test with encoder class:")
    result = json.dumps(test, cls=TestEncoder)
    print(result)

我无法成功编码字典(使用Python 3.6.1) 。我不断收到 TypeError:键必须是字符串错误和我的自定义编码器实例的默认方法(通过 cls json.dumps 方法的参数)似乎从未被调用过?我还尝试通过 json.dumps 方法的 default 参数提供自定义编码方法,但是再次

I cannot successfully encode the dictionary (using Python 3.6.1). I continually get TypeError: keys must be a string errors and the default method of my custom encoder instance (supplied via the cls argument of the json.dumps method) never seems to be called? I have also attempted to supply a custom encoding method via the default argument of the json.dumps method, but again this is never triggered.

我见过涉及IntEnum类的解决方案,但是我需要Enum的值是字符串。我还看到了此答案,其中讨论了与从另一个继承的枚举有关的问题类。但是,我的枚举仅继承自基本枚举类。Enum类只能正确响应 isinstance 调用吗?

I have seen solutions involving the IntEnum class, but I need the values of the Enum to be strings. I have also seen this answer which discusses an issue related to an Enum which inherits from another class. However, my enums inherit from the base enum.Enum class only and correctly respond to isinstance calls?

自定义类和方法在提供给 json时均会产生 TypeError 。转储方法。典型输出如下所示:

Both the custom class and the method produce a TypeError when supplied to the json.dumps method. Typical output is shown below:

$ python3 enum_test.py

Test with encoder class
Traceback (most recent call last):
  File "enum_test.py", line 59, in <module>
    result = json.dumps(test, cls=TestEncoder)
  File "/usr/lib64/python3.6/json/__init__.py", line 238, in dumps
    **kw).encode(obj)
  File "/usr/lib64/python3.6/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/lib64/python3.6/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
TypeError: keys must be a string

我认为问题是 encode 方法JSONEncoder类假定它知道如何序列化Enum类(因为触发了 iterencode 方法中的if语句之一),因此永远不要调用自定义默认方法并结束无法序列化枚举?

I presume the issue is that the encode method of the JSONEncoder class assumes that it knows how to serialise the Enum class (because one of the if statements in the iterencode method is triggered) and so never calls the custom default methods and ends failing to serialise the Enum?

任何帮助将不胜感激!

推荐答案

您可以在要转换为JSON的字典中,不要使用 strings 作为键。编码器没有其他选择。 default 钩子仅用于未知类型的值,而不用于键。

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))

从JSON加载时,可以使用相同的功能来逆转该过程:

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 key

convert_keys(json.loads(json_data), names_to_enum)

演示:

>>> 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!'}

这篇关于将Python枚举编码为JSON的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆