JSON to Python
Генерация Python dataclass из JSON
Ввод JSON
Вывод Python
Что такое конвертация JSON в Python dataclass?
Конвертация JSON в Python dataclass берёт исходный JSON-объект и создаёт набор определений Python dataclass с точными аннотациями типов. Модуль dataclasses, появившийся в PEP 557 (Python 3.7), автоматически генерирует методы __init__, __repr__ и __eq__ из аннотированных полей класса. При работе с JSON API, конфигурационными файлами или очередями сообщений dataclasses придают данным типизированную структуру, которую редакторы и системы проверки типов, такие как mypy, могут верифицировать на этапе разработки.
Python's json.loads() возвращает обычные словари и списки. Они работают, но не несут информации о типах: опечатка в имени ключа вернёт None вместо исключения, а редактор не сможет предложить автодополнение. Dataclasses решают эту проблему, сопоставляя каждый ключ JSON с именованным типизированным полем. Вложенные JSON-объекты становятся отдельными определениями dataclass, массивы — аннотациями List[T], а null-значения — Optional[T] со значением по умолчанию None.
Писать эти определения вручную — механическая работа. Нужно прочитать JSON, определить тип каждого поля по его значению, преобразовать ключи из camelCase или snake_case в соответствии с соглашениями Python и обработать граничные случаи, такие как nullable-поля и массивы смешанных типов. Конвертер делает всё это за миллисекунды: вставляете JSON — получаете корректный код dataclass.
Зачем использовать конвертер JSON в Python?
Переводить JSON-структуры в определения Python-классов вручную — значит угадывать типы по примерам данных, переставлять поля так, чтобы обязательные шли раньше опциональных, и обновлять всё при изменении API. Конвертер устраняет это неудобство.
Сценарии использования JSON to Python
Таблица соответствия типов JSON и 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. Каждый подходит для своего сценария. Dataclasses — вариант стандартной библиотеки без зависимостей. Pydantic добавляет валидацию во время выполнения. TypedDict аннотирует обычные словари, не создавая новый класс.
Примеры кода
Эти примеры показывают, как использовать сгенерированные dataclasses в 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]