« ^ »

PythonのEnumをjson.dumpでJSON形式に変換する

所要時間: 約 2分

Pythonのjsonモジュールを使ってJSON化する際、その中の値にJSONに変換できない値があるとTypeErrorとなってしまう。

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/ng/symdon/pages/posts/1616552904/example.py", line 27, in <module>
    dumps(
  File "/usr/local/Cellar/[email protected]/3.8.8_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "/usr/local/Cellar/[email protected]/3.8.8_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "/usr/local/Cellar/[email protected]/3.8.8_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "/usr/local/Cellar/[email protected]/3.8.8_1/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type Example1Enum is not JSON serializable

型ヒントを有効に使おうとするとEnumの値はそのまま保持したほうがよいため json.dumps()する際にEnumの値が含まれているケースがある。そのためEnumの 値をJSON化する場合には.valueで値を取得しそれをJSON化するための JSONEncoderを書いた。

  from json import JSONEncoder


  class EnumAvailableJSONEncoder(JSONEncoder):
      """Enumクラスの値を.valueの値にしてエンコードする"""

      def default(self, o):
          if isinstance(o, Enum):
             return o.value
          return super().default(o)

isinstance()でEnumのインスタンスかどうかを確認しそうであれば .value で値を取得、そうでなければそのままの値をJSON化している。

例としてEnumを2つ実装し、それらを変換できるかを確認する。

  from json import JSONEncoder
  from enum import Enum
  from json import dumps


  class EnumAvailableJSONEncoder(JSONEncoder):
      """Enumクラスの値を.valueの値にしてエンコードする"""

      def default(self, o):
          if isinstance(o, Enum):
             return o.value
          return super().default(o)


  class Example1Enum(Enum):
      foo = "Foo"
      bar = "Bar"
      baz = "Baz"


  class Example2Enum(Enum):
      foo = "Foo"
      bar = "Bar"
      baz = "Baz"


  return dumps(
      {"ex1": Example1Enum.foo, "ex2": Example1Enum.bar},
      cls=EnumAvailableJSONEncoder,
  )

{"ex1": "Foo", "ex2": "Bar"}

期待どおりに変換された。