2017-05-08 10 views
3

私はいくつかのキーがEnumインスタンス(enum.Enumのサブクラス)である辞書を持っています。私はdocumentationごとにカスタムJSONエンコーダクラスを使用してJSON文字列に辞書をエンコードしようとしています。私が望むのは、出力JSONのキーをEnum名の文字列にすることだけです。たとえば{ TestEnum.one : somevalue }{ "one" : somevalue }にコードされます。Python Enum to JSONのエンコーディング

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) 

Iが正常に(Pythonの3.6.1を使用して)辞書をエンコードすることができない。

私はクリーンvirtualenvので試験した以下に示す簡単なテストケースを、書かれています。私は継続的にTypeError: keys must be a stringエラーを取得し、私のカスタムエンコーダインスタンスのcls引数(json.dumpsメソッドの引数)のデフォルトメソッドは、決して呼び出されないようです。また、私はjson.dumpsメソッドのdefault引数でカスタムエンコードメソッドを提供しようとしましたが、これは決してトリガーされません。

私はIntEnumクラスを含むソリューションを見てきましたが、Enumの値を文字列にする必要があります。私はthis answerも見てきましたが、これは別のクラスから継承するEnumに関連する問題を論じています。しかし、私の列挙型は基本列挙型を継承しています.Enumクラスのみが正しくisinstance呼び出しに応答しますか?

json.dumpsメソッドに提供されるとき、カスタムクラスとメソッドの両方がTypeErrorを生成します。典型的な出力は次のとおりです:私は問題がJSONEncoderクラスのencode方法は、それが列挙型クラスをシリアライズする方法を知っていることを前提としていることであると推定

$ 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 

iterencode方法でif文のいずれかがトリガされるため)カスタムのデフォルトメソッドを呼び出すことはなく、Enumのシリアル化に失敗しますか?

ご協力いただければ幸いです!

+0

:http://stackoverflow.com/a/43730306/674039 – wim

+0

文字列以外のキーは使用できません。エンコーダはそこで仲裁することはありませんが、それは難しいルールです。 –

答えて

4

文字列をJSONに変換する辞書のキーとして使用することはできません。エンコーダは他のオプションを提供しません。 defaultフックは不明な型の値に対してのみ呼び出され、キー用には呼び出されません。

フロントを文字列にあなたの鍵を変換します

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

これは再帰的にあなたの辞書のキーを処理します。私はフックを含むことに注意してください。あなたはその後、文字列に列挙値を変換する方法を選択することができます。

def enum_names(key): 
    if isinstance(key, TestEnum): 
     return key.name 
    return str(key) 

json.dumps(convert_keys(test, enum_names)) 

あなたは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!'} 
+0

それはうまくいった!ありがとうございました。 Encoderを使ってこれを行う方法がないことは残念ですが、ドキュメントについてはこれほど明確ではありません。 –

関連する問題