JSON به Python Dataclass
تولید dataclass های Python از JSON
ورودی JSON
خروجی Python
تبدیل JSON به Python Dataclass چیست؟
تبدیل JSON به dataclass در Python یک شیء JSON خام را دریافت کرده و مجموعهای از تعریف dataclass های Python با type annotation های دقیق تولید میکند. ماژول dataclasses در Python که در PEP 557 (Python 3.7) معرفی شد، متدهای __init__، __repr__ و __eq__ را از فیلدهای annotated کلاس تولید میکند. وقتی با JSON API ها، فایلهای پیکربندی یا صفهای پیام کار میکنید، dataclass ها ساختاری نوعبندیشده به دادههای شما میدهند که ویرایشگرها و ابزارهای بررسی نوع مانند mypy میتوانند در زمان توسعه آن را تأیید کنند.
تابع json.loads() در Python دیکشنریها و لیستهای ساده برمیگرداند. اینها کار میکنند، اما اطلاعات نوع ندارند: یک کلید اشتباه تایپشده بهجای ایجاد خطا مقدار None برمیگرداند، و ویرایشگر شما نمیتواند نام فیلدها را تکمیل خودکار کند. Dataclass ها این مشکل را با نگاشت هر کلید JSON به یک فیلد نامگذاریشده و نوعبندیشده حل میکنند. اشیاء JSON تودرتو به تعریفهای dataclass جداگانه تبدیل میشوند، آرایهها به annotation های List[T]، و مقادیر null به Optional[T] با مقدار پیشفرض None.
نوشتن این تعریفها بهصورت دستی کار مکانیکی است. باید JSON را بخوانید، نوع هر فیلد را از مقدارش تشخیص دهید، کلیدها را از camelCase یا snake_case به قراردادهای Python تبدیل کنید، و موارد لبهای مانند فیلدهای nullable و آرایههای با نوع ترکیبی را مدیریت کنید. یک مولد همه اینها را در میلیثانیه انجام میدهد. JSON را پیست کنید، کد dataclass صحیح دریافت کنید، و ادامه دهید.
چرا از مولد JSON به Python استفاده کنیم؟
تبدیل دستی ساختارهای JSON به تعریف کلاسهای Python به معنای حدس زدن نوعها از دادههای نمونه، مرتب کردن فیلدها بهطوری که فیلدهای اجباری قبل از اختیاری بیایند، و بهروزرسانی همه چیز وقتی API تغییر میکند. یک مولد این دردسر را برطرف میکند.
موارد استفاده JSON به Python
جدول نگاشت نوع JSON به Python
هر مقدار JSON به یک type annotation خاص در 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] |
مرجع decorator های dataclass
decorator ی @dataclass چندین پارامتر میپذیرد که نحوه رفتار کلاس تولیدشده را تغییر میدهند. این مرجع گزینههایی را پوشش میدهد که هنگام کار با دادههای مشتقشده از JSON بیشترین کاربرد را دارند.
| Decorator / فیلد | رفتار | زمان استفاده |
|---|---|---|
| @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 در مقابل Pydantic در مقابل TypedDict
Python سه روش رایج برای تعریف ساختارهای نوعبندیشده از JSON دارد. هر کدام برای یک مورد استفاده متفاوت مناسب است. Dataclass ها گزینه کتابخانه استاندارد با صفر وابستگی هستند. Pydantic اعتبارسنجی در زمان اجرا اضافه میکند. TypedDict دیکشنریهای ساده را بدون ایجاد کلاس جدید annotate میکند.
نمونه کد
این مثالها نشان میدهند چگونه از dataclass های تولیدشده در Python استفاده کنید، چگونه آنها را بهصورت برنامهنویسی از 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]