Created
August 20, 2024 05:06
-
-
Save milhidaka/4d8262e4fafea739b04f995859c4d26b to your computer and use it in GitHub Desktop.
Python 3.11 NamedTuple <-> JSON 型情報を指定したシリアライズ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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