JSON → Python 클래스 변환기
JSON에서 타입이 지정된 Python 클래스를 생성하고, 파싱과 직렬화를 위한 from_dict·to_dict 메서드도 함께 제공합니다.
JSON에서 타입이 지정된 Python 클래스 생성
json.loads는 중첩된 dict를 반환하기 때문에 키 오타는 런타임에야 드러나고 에디터 자동완성도 쓸 수 없습니다. 이 변환기는 JSON의 객체마다 클래스를 만들어 줍니다. 각 클래스에는 타입 어노테이션이 붙은 속성(str, int, float, bool, List[...]), 파싱하면서 각 값의 타입을 확인하는 from_dict 정적 메서드, JSON으로 내보내기 위해 객체를 다시 dict로 바꾸는 to_dict 메서드가 포함됩니다. 표준 라이브러리의 typing 모듈만 사용합니다.
사용 방법
- 웹훅 페이로드나 API 응답 같은 JSON 샘플을 붙여넣습니다.
- 루트 클래스 이름을 지정합니다. 예: Order.
- 코드를 모듈로 저장하고 Order.from_dict(json.loads(text))로 데이터를 파싱합니다. 다시 전송할 때는 json.dumps 전에 to_dict()를 호출하세요.
자주 묻는 질문
- dataclass인가요, Pydantic 모델인가요?
- 둘 다 아닙니다. 타입 힌트와 명시적인 from_dict/to_dict 메서드를 가진 일반 클래스이므로 서드파티 패키지가 필요 없습니다. 원한다면 @dataclass를 붙이거나, 프로젝트에서 이미 Pydantic을 쓰고 있다면 필드를 Pydantic 모델로 옮겨도 됩니다.
- 데이터가 타입과 맞지 않으면 어떻게 되나요?
- 생성된 헬퍼 함수는 assert isinstance로 검사하므로, 값의 타입이 예상과 다르면 from_dict에서 AssertionError가 발생합니다. 단, Python을 -O 옵션으로 실행하면 assert가 건너뛰어진다는 점에 유의하세요.
- 어떤 Python 버전이 필요한가요?
- typing 모듈의 어노테이션을 사용하며 Python 3.7 이상에서 동작합니다.
예시: JSON to Python
입력 JSON
{
"order_id": "A-1001",
"total": 59.9,
"paid": true,
"items": [{ "sku": "BOOK-1", "quantity": 2 }]
}생성된 Python 코드 (Order)
from typing import Any, List, TypeVar, Callable, Type, cast
T = TypeVar("T")
def from_str(x: Any) -> str:
assert isinstance(x, str)
return x
def from_int(x: Any) -> int:
assert isinstance(x, int) and not isinstance(x, bool)
return x
def from_float(x: Any) -> float:
assert isinstance(x, (float, int)) and not isinstance(x, bool)
return float(x)
def from_bool(x: Any) -> bool:
assert isinstance(x, bool)
return x
def from_list(f: Callable[[Any], T], x: Any) -> List[T]:
assert isinstance(x, list)
return [f(y) for y in x]
def to_float(x: Any) -> float:
assert isinstance(x, (int, float))
return x
def to_class(c: Type[T], x: Any) -> dict:
assert isinstance(x, c)
return cast(Any, x).to_dict()
class Item:
sku: str
quantity: int
def __init__(self, sku: str, quantity: int) -> None:
self.sku = sku
self.quantity = quantity
@staticmethod
def from_dict(obj: Any) -> 'Item':
assert isinstance(obj, dict)
sku = from_str(obj.get("sku"))
quantity = from_int(obj.get("quantity"))
return Item(sku, quantity)
def to_dict(self) -> dict:
result: dict = {}
result["sku"] = from_str(self.sku)
result["quantity"] = from_int(self.quantity)
return result
class Order:
order_id: str
total: float
paid: bool
items: List[Item]
def __init__(self, order_id: str, total: float, paid: bool, items: List[Item]) -> None:
self.order_id = order_id
self.total = total
self.paid = paid
self.items = items
@staticmethod
def from_dict(obj: Any) -> 'Order':
assert isinstance(obj, dict)
order_id = from_str(obj.get("order_id"))
total = from_float(obj.get("total"))
paid = from_bool(obj.get("paid"))
items = from_list(Item.from_dict, obj.get("items"))
return Order(order_id, total, paid, items)
def to_dict(self) -> dict:
result: dict = {}
result["order_id"] = from_str(self.order_id)
result["total"] = to_float(self.total)
result["paid"] = from_bool(self.paid)
result["items"] = from_list(lambda x: to_class(Item, x), self.items)
return result
def order_from_dict(s: Any) -> Order:
return Order.from_dict(s)
def order_to_dict(x: Order) -> Any:
return to_class(Order, x)