JSON to Python Dataclass 생성기
JSON에서 Python dataclass 생성
JSON 입력
Python 출력
JSON to Python Dataclass 변환이란?
JSON to Python dataclass 변환은 원시 JSON 객체를 받아 정확한 타입 어노테이션이 포함된 Python dataclass 정의 세트를 생성합니다. PEP 557(Python 3.7)에서 도입된 Python의 dataclasses 모듈은 어노테이션이 달린 클래스 필드에서 __init__, __repr__, __eq__ 메서드를 자동으로 생성합니다. JSON API, 설정 파일, 메시지 큐를 다룰 때 dataclass는 데이터에 타입이 있는 구조를 부여하여 에디터와 mypy 같은 타입 검사기가 개발 시점에 검증할 수 있게 합니다.
Python의 json.loads()는 일반 dict와 list를 반환합니다. 이것도 동작하지만 타입 정보가 없습니다. 오타가 있는 키는 오류를 발생시키는 대신 None을 반환하고, 에디터는 필드 이름을 자동 완성할 수 없습니다. Dataclass는 각 JSON 키를 이름 있는 타입 지정 필드에 매핑하여 이 문제를 해결합니다. 중첩된 JSON 객체는 별도의 dataclass 정의가 되고, 배열은 List[T] 어노테이션이 되며, null 값은 기본값 None을 가진 Optional[T]가 됩니다.
이러한 정의를 수동으로 작성하는 것은 기계적인 작업입니다. JSON을 읽고, 값에서 각 필드의 타입을 파악하고, camelCase 또는 snake_case에서 Python 관례로 키를 변환하고, nullable 필드나 혼합 타입 배열 같은 엣지 케이스를 처리해야 합니다. 변환기는 이 모든 것을 밀리초 안에 처리합니다. JSON을 붙여넣으면 올바른 dataclass 코드를 얻고 바로 작업을 계속할 수 있습니다.
JSON to Python 변환기를 사용하는 이유
JSON 구조를 Python 클래스 정의로 수동 변환하면 샘플 데이터에서 타입을 추측하고, 선택적 필드보다 필수 필드가 먼저 오도록 필드를 재정렬하고, API가 변경될 때마다 모든 것을 업데이트해야 합니다. 변환기는 이러한 불편함을 제거합니다.
JSON to Python 활용 사례
JSON to Python 타입 매핑
모든 JSON 값은 특정 Python 타입 어노테이션에 매핑됩니다. 아래 표는 변환기가 각 JSON 타입을 어떻게 변환하는지 typing 모듈 문법(Python 3.7+)과 Python 3.10부터 사용 가능한 내장 문법 모두로 보여줍니다.
| JSON 타입 | 예시 | Python (typing) | Python 3.10+ |
|---|---|---|---|
| string | "hello" | str | str |
| number (integer) | 42 | int | int |
| number (float) | 3.14 | float | float |
| boolean | true | bool | bool |
| null | null | Optional[str] | str | None |
| object | {"k": "v"} | @dataclass class | nested model |
| array of strings | ["a", "b"] | List[str] | list[str] |
| array of objects | [{"id": 1}] | List[Item] | list[Item] |
| mixed array | [1, "a"] | List[Any] | list[Any] |
Dataclass 데코레이터 참조
@dataclass 데코레이터는 생성된 클래스의 동작을 변경하는 여러 파라미터를 받습니다. 이 참조는 JSON에서 파생된 데이터를 다룰 때 가장 관련성 높은 옵션을 다룹니다.
| 데코레이터 / 필드 | 동작 | 사용 시점 |
|---|---|---|
| @dataclass | Generates __init__, __repr__, __eq__ from field annotations | Standard dataclasses |
| @dataclass(frozen=True) | Makes instances immutable (hashable, no attribute reassignment) | Config objects, dict keys |
| @dataclass(slots=True) | Uses __slots__ for lower memory and faster attribute access | Python 3.10+, large datasets |
| @dataclass(kw_only=True) | All fields require keyword arguments in __init__ | Python 3.10+, many fields |
| field(default_factory=list) | Sets a mutable default without sharing state between instances | List/dict/set defaults |
dataclass vs Pydantic vs TypedDict
Python에는 JSON에서 타입이 지정된 구조를 정의하는 세 가지 일반적인 방법이 있습니다. 각각 다른 사용 사례에 적합합니다. Dataclass는 외부 의존성이 없는 표준 라이브러리 옵션입니다. Pydantic은 런타임 검증을 추가합니다. TypedDict는 새 클래스를 생성하지 않고 일반 dict에 어노테이션을 추가합니다.
코드 예제
이 예제들은 Python에서 생성된 dataclass를 사용하는 방법, JavaScript로 프로그래밍 방식으로 생성하는 방법, Pydantic 및 CLI 도구 같은 대안적 접근 방식을 보여줍니다.
from dataclasses import dataclass
from typing import List, Optional
import json
@dataclass
class Address:
street: str
city: str
zip: str
@dataclass
class User:
id: int
name: str
email: str
active: bool
score: float
address: Address
tags: List[str]
metadata: Optional[str] = None
raw = '{"id":1,"name":"Alice","email":"alice@example.com","active":true,"score":98.5,"address":{"street":"123 Main St","city":"Springfield","zip":"12345"},"tags":["admin","user"],"metadata":null}'
data = json.loads(raw)
# Reconstruct nested objects manually
addr = Address(**data["address"])
user = User(**{**data, "address": addr})
print(user.name) # -> Alice
print(user.address) # -> Address(street='123 Main St', city='Springfield', zip='12345')// Minimal JSON-to-Python-dataclass generator in JS
function jsonToPython(obj, name = "Root") {
const classes = [];
function infer(val, fieldName) {
if (val === null) return "Optional[str]";
if (typeof val === "string") return "str";
if (typeof val === "number") return Number.isInteger(val) ? "int" : "float";
if (typeof val === "boolean") return "bool";
if (Array.isArray(val)) {
const first = val.find(v => v !== null);
return first ? `List[${infer(first, fieldName + "Item")}]` : "List[Any]";
}
if (typeof val === "object") {
const clsName = fieldName.charAt(0).toUpperCase() + fieldName.slice(1);
build(val, clsName);
return clsName;
}
return "Any";
}
function build(obj, cls) {
const fields = Object.entries(obj).map(([k, v]) => ` ${k}: ${infer(v, k)}`);
classes.push(`@dataclass\nclass ${cls}:\n${fields.join("\n")}`);
}
build(obj, name);
return classes.join("\n\n");
}
const data = { id: 1, name: "Alice", scores: [98, 85] };
console.log(jsonToPython(data, "User"));
// @dataclass
// class User:
// id: int
// name: str
// scores: List[int]from pydantic import BaseModel
from typing import List, Optional
class Address(BaseModel):
street: str
city: str
zip: str
class User(BaseModel):
id: int
name: str
email: str
active: bool
score: float
address: Address
tags: List[str]
metadata: Optional[str] = None
# Pydantic parses and validates JSON in one step
raw = '{"id":1,"name":"Alice","email":"alice@example.com","active":true,"score":98.5,"address":{"street":"123 Main St","city":"Springfield","zip":"12345"},"tags":["admin","user"],"metadata":null}'
user = User.model_validate_json(raw)
print(user.name) # -> Alice
print(user.model_dump_json()) # -> re-serializes to JSON# Install the generator
pip install datamodel-code-generator
# Generate dataclasses from a JSON file
datamodel-codegen --input data.json --output models.py --output-model-type dataclasses.dataclass
# Generate Pydantic models instead
datamodel-codegen --input data.json --output models.py
# From a JSON string via stdin
echo '{"id": 1, "name": "Alice", "tags": ["admin"]}' | \
datamodel-codegen --output-model-type dataclasses.dataclass
# Output:
# @dataclass
# class Model:
# id: int
# name: str
# tags: List[str]