Skip to content

Instantly share code, notes, and snippets.

@milhidaka
Created August 20, 2024 05:06
Show Gist options
  • Save milhidaka/4d8262e4fafea739b04f995859c4d26b to your computer and use it in GitHub Desktop.
Save milhidaka/4d8262e4fafea739b04f995859c4d26b to your computer and use it in GitHub Desktop.
Python 3.11 NamedTuple <-> JSON 型情報を指定したシリアライズ
import json
from typing import NamedTuple, get_origin, get_args
def dict_to_namedtuple(d, namedtuple_type):
if isinstance(d, dict):
# ネストされた辞書を再帰的にnamedtupleに変換
return namedtuple_type(**{k: dict_to_namedtuple(v, namedtuple_type.__annotations__[k])
for k, v in d.items()})
elif isinstance(d, list):
# リスト内の要素を再帰的に変換
# named_typle_type: list[SomeType]
assert get_origin(namedtuple_type) is list
item_type = get_args(namedtuple_type)[0]
return [dict_to_namedtuple(item, item_type) for item in d]
else:
return d
# namedtupleを定義
class Point(NamedTuple):
x: int
y: int
class Line(NamedTuple):
start: Point
end: Point
class Points(NamedTuple):
items: list[Point]
line_dict = json.loads('{"start": {"x": 10, "y": 20}, "end": {"x": 30, "y": 40}}')
line = dict_to_namedtuple(line_dict, Line)
# => Line(start=Point(x=10, y=20), end=Point(x=30, y=40))
points_dict = json.loads('{"items":[{"x":10,"y":20},{"x":30,"y":40}]}')
points = dict_to_namedtuple(points_dict, Points)
# => Points(items=[Point(x=10, y=20), Point(x=30, y=40)])
import json
from typing import NamedTuple
from collections import namedtuple, abc
def namedtuple_to_dict(obj):
if isinstance(obj, tuple) and hasattr(obj, '_fields'):
# namedtuple を辞書に変換
return {key: namedtuple_to_dict(value) for key, value in obj._asdict().items()}
elif isinstance(obj, list):
# リストの各要素を再帰的に処理
return [namedtuple_to_dict(item) for item in obj]
elif isinstance(obj, dict):
# 辞書の各要素を再帰的に処理
return {key: namedtuple_to_dict(value) for key, value in obj.items()}
else:
# 他のオブジェクトはそのまま返す
return obj
# namedtupleを定義
class Point(NamedTuple):
x: int
y: int
class Line(NamedTuple):
start: Point
end: Point
class Points(NamedTuple):
items: list[Point]
# 実行例
p1 = Point(10, 20)
p2 = Point(30, 40)
line = Line(start=p1, end=p2)
json.dumps(namedtuple_to_dict(line))
# => '{"start": {"x": 10, "y": 20}, "end": {"x": 30, "y": 40}}'
pts = Points(items=[Point(10,20),Point(30,40)])
json.dumps(namedtuple_to_dict(pts))
# => '{"items": [{"x": 10, "y": 20}, {"x": 30, "y": 40}]}'
# 注意: JSONEncoderのdefaultメソッドのオーバーライドでは失敗する。namedtupleはlistとして処理可能なため、オーバーライドしたメソッドが呼ばれずリストとしてシリアライズされてしまう。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment