JSON sang Python
Tạo Python dataclasses từ JSON
Đầu vào JSON
Đầu ra Python
Chuyển đổi JSON sang Python Dataclass là gì?
Chuyển đổi JSON sang Python dataclass lấy một đối tượng JSON thô và tạo ra tập hợp các định nghĩa Python dataclass với type annotations chính xác. Module dataclasses của Python, được giới thiệu trong PEP 557 (Python 3.7), tự động tạo ra các phương thức __init__, __repr__ và __eq__ từ các trường class được chú thích. Khi làm việc với JSON APIs, file cấu hình hoặc hàng đợi tin nhắn, dataclasses cung cấp cho dữ liệu một cấu trúc có kiểu mà các trình soạn thảo và công cụ kiểm tra kiểu như mypy có thể xác minh trong quá trình phát triển.
Hàm json.loads() của Python trả về các dict và list thông thường. Chúng hoạt động được, nhưng không có thông tin kiểu: một key viết sai trả về None thay vì báo lỗi, và trình soạn thảo không thể tự động hoàn thành tên trường. Dataclasses giải quyết vấn đề này bằng cách ánh xạ mỗi key JSON thành một trường có tên và có kiểu. Các đối tượng JSON lồng nhau trở thành các định nghĩa dataclass riêng biệt, mảng trở thành các chú thích List[T], và giá trị null trở thành Optional[T] với giá trị mặc định là None.
Viết các định nghĩa này bằng tay là công việc lặp đi lặp lại. Bạn phải đọc JSON, xác định kiểu của từng trường từ giá trị của nó, chuyển đổi key từ camelCase hoặc snake_case sang quy ước Python, và xử lý các trường hợp đặc biệt như trường nullable và mảng kiểu hỗn hợp. Một bộ chuyển đổi thực hiện tất cả điều này trong vài mili giây. Bạn dán JSON, nhận code dataclass chính xác và tiếp tục công việc.
Tại sao nên dùng bộ chuyển đổi JSON sang Python?
Chuyển đổi thủ công cấu trúc JSON thành định nghĩa class Python đồng nghĩa với việc đoán kiểu từ dữ liệu mẫu, sắp xếp lại trường để trường bắt buộc đứng trước trường tùy chọn, và cập nhật lại tất cả khi API thay đổi. Bộ chuyển đổi loại bỏ những trở ngại đó.
Các trường hợp sử dụng JSON sang Python
Bảng ánh xạ kiểu JSON sang Python
Mỗi giá trị JSON ánh xạ sang một type annotation Python cụ thể. Bảng dưới đây cho thấy bộ chuyển đổi dịch từng kiểu JSON như thế nào, với cú pháp module typing (Python 3.7+) và cú pháp tích hợp có sẵn từ Python 3.10 trở lên.
| Kiểu JSON | Ví dụ | 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] |
Tài liệu tham khảo Decorator Dataclass
Decorator @dataclass chấp nhận nhiều tham số thay đổi cách class được tạo hoạt động. Tài liệu này bao gồm các tùy chọn phù hợp nhất khi làm việc với dữ liệu được lấy từ JSON.
| Decorator / Trường | Hành vi | Dùng khi |
|---|---|---|
| @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 |
So sánh dataclass, Pydantic và TypedDict
Python có ba cách phổ biến để định nghĩa cấu trúc có kiểu từ JSON. Mỗi cách phù hợp với một trường hợp sử dụng khác nhau. Dataclasses là tùy chọn thư viện chuẩn không có phụ thuộc bên ngoài. Pydantic bổ sung validation tại runtime. TypedDict chú thích các dict thông thường mà không tạo class mới.
Ví dụ code
Các ví dụ này cho thấy cách dùng dataclasses được tạo ra trong Python, cách tạo chúng theo chương trình từ JavaScript, và cách dùng các phương pháp thay thế như Pydantic và công cụ 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]