DevToolBoxFREE
BlogAdvertise

JSON zu Python Dataclass: Pydantic, dataclasses, TypedDict & attrs Anleitung

17 Min. Lesezeitvon DevToolBox

Die Konvertierung von JSON in Python-Dataclasses ist eine der haufigsten Aufgaben in der modernen Python-Entwicklung. Wenn Ihre Anwendung eine JSON-Antwort von einer REST-API erhalt, benotigen Sie gut strukturierte Python-Dataclasses, Pydantic-Modelle oder TypedDicts. Dieser Leitfaden behandelt Typ-Mapping, JSON-Parsing-Strategien, Pydantic, attrs, TypedDict und Best Practices.

Testen Sie unseren kostenlosen Online JSON-zu-Python-Dataclass-Konverter.

Was ist JSON-zu-Python-Dataclass-Konvertierung?

JSON ist das dominierende Datenaustauschformat fur Web-APIs. Python profitiert enorm von expliziten Typdefinitionen uber Dataclasses, Pydantic-Modelle und TypedDicts. Die JSON-zu-Python-Konvertierung analysiert ein JSON-Dokument und erzeugt entsprechende Python-Klassen.

In einer typischen FastAPI-Anwendung empfangt ein Route-Handler den HTTP-Request-Body als JSON-String. Das Framework muss dieses JSON mit Pydantics BaseModel in Python-Objekte konvertieren. Ein JSON-zu-Python-Konverter automatisiert die Erstellung dieser Datenmodelle.

Die gleiche Konvertierung ist in Django REST Framework, in Datenpipelines und in CLI-Tools wesentlich. Der Prozess ist identisch: JSON-Struktur inspizieren, Feldtypen bestimmen, Verschachtelung und Arrays behandeln.

JSON zu Python: Typ-Mapping

Das Verstandnis der Zuordnung von JSON-Typen zu Python-Typen ist die Grundlage:

JSON-TypBeispielPython-Typ(en)Hinweise
string"hello"strImmer str; datetime fur ISO-Daten
number (ganzzahlig)42intBeliebige Prazision in Python
number (dezimal)3.14float, DecimalDecimal fur Finanzdaten
booleantrueboolDirekte Zuordnung
nullnullNoneOptional[T] oder T | None
array[1,2]list[T]list[T] ab Python 3.9+
object{"k":"v"}Verschachtelte KlasseStark typisierte Klassen bevorzugt

Bei der Generierung von Python-Dataclasses aus JSON hangt die Wahl zwischen Optional[str] und str | None von der Python-Version ab. Fur Geldwerte immer Decimal verwenden.

Wie die JSON-zu-Python-Konvertierung funktioniert

Ein JSON-zu-Python-Dataclass-Konverter folgt einem systematischen Prozess:

  1. JSON-Struktur parsen: Parsing mit Pythons eingebautem json-Modul.
  2. Feldtypen ableiten: Python-Typ fur jedes Schlussel-Wert-Paar bestimmen.
  3. Klassennamen generieren: Konvertierung in PascalCase und snake_case.
  4. Verschachtelte Objekte behandeln: Jedes Objekt erzeugt eine separate Klasse.
  5. Arrays behandeln: Elementtyp-Analyse.
  6. Typ-Annotationen hinzufugen: PEP 484/604 Syntax.
  7. Quellcode ausgeben: Formatierter Python-Code.

Codebeispiele: JSON zu Python mit Pydantic, dataclasses, TypedDict und attrs

Pydantic v2: BaseModel, Field und Validatoren

Pydantic ist die am weitesten verbreitete JSON-zu-Python-Bibliothek. FastAPI verwendet sie standardmassig:

# === Sample JSON ===
# {
#   "user_id": 1001,
#   "user_name": "Alice",
#   "email": "alice@example.com",
#   "is_active": true,
#   "balance": 1250.75,
#   "tags": ["admin", "developer"],
#   "address": {
#     "street": "123 Main St",
#     "city": "Springfield",
#     "zip_code": "62704"
#   }
# }

# === Pydantic v2 Models ===
from pydantic import BaseModel, Field, field_validator, ConfigDict
from typing import Optional


class Address(BaseModel):
    street: str
    city: str
    zip_code: str = Field(alias="zipCode")

    model_config = ConfigDict(populate_by_name=True)


class User(BaseModel):
    user_id: int
    user_name: str
    email: str
    is_active: bool = True
    balance: float = 0.0
    tags: list[str] = []
    address: Optional[Address] = None

    @field_validator("email")
    @classmethod
    def validate_email(cls, v: str) -> str:
        if "@" not in v:
            raise ValueError("Invalid email address")
        return v.lower().strip()

    model_config = ConfigDict(populate_by_name=True)


# === Parsing JSON string directly ===
json_string = '{"user_id": 1001, "user_name": "Alice", ...}'
user = User.model_validate_json(json_string)

# === Parsing from dict ===
data = {"user_id": 1001, "user_name": "Alice", "email": "alice@example.com"}
user = User.model_validate(data)

# === Serialization ===
print(user.model_dump())          # dict output
print(user.model_dump_json())     # JSON string output

# === List of models ===
from pydantic import TypeAdapter

users_adapter = TypeAdapter(list[User])
users = users_adapter.validate_json(json_array_string)

dataclasses + json: @dataclass, dataclass_json, dacite

Pythons eingebautes dataclasses-Modul bietet eine leichtgewichtige Alternative:

from dataclasses import dataclass, field
from typing import Optional
import json

# === Standard dataclass ===
@dataclass
class Address:
    street: str
    city: str
    zip_code: str

@dataclass
class User:
    user_id: int
    user_name: str
    email: str
    is_active: bool = True
    balance: float = 0.0
    tags: list[str] = field(default_factory=list)
    address: Optional[Address] = None

    def __post_init__(self):
        # Convert nested dict to Address if needed
        if isinstance(self.address, dict):
            self.address = Address(**self.address)

# === Parse from JSON ===
raw = json.loads(json_string)
user = User(**raw)

# === Using dacite for nested structures ===
import dacite

data = json.loads(json_string)
user = dacite.from_dict(data_class=User, data=data)
# dacite handles nested dicts, Optional, Union automatically

# === Using dataclasses-json ===
from dataclasses_json import dataclass_json, LetterCase, config

@dataclass_json(letter_case=LetterCase.CAMEL)
@dataclass
class Product:
    product_id: int
    product_name: str
    unit_price: float
    in_stock: bool = True

# Direct JSON parsing
product = Product.from_json('{"productId": 42, "productName": "Keyboard", ...}')

# Direct JSON serialization
json_output = product.to_json()

# === Frozen (immutable) dataclass ===
@dataclass(frozen=True, slots=True)
class ImmutableConfig:
    host: str
    port: int
    debug: bool = False

TypedDict: Typ-Hinweise ohne Laufzeit-Overhead

Wenn Sie Python-JSON-Parsing mit Typsicherheit aber null Laufzeit-Overhead benotigen:

from typing import TypedDict, NotRequired
import json

# === TypedDict definitions ===
class Address(TypedDict):
    street: str
    city: str
    zip_code: str

class User(TypedDict):
    user_id: int
    user_name: str
    email: str
    is_active: bool
    balance: float
    tags: list[str]
    address: NotRequired[Address]  # Python 3.11+

# === Parse JSON with type safety ===
raw: str = '{"user_id": 1001, "user_name": "Alice", ...}'
data: User = json.loads(raw)  # type checker knows the shape

# Access with full IDE autocompletion
print(data["user_name"])   # str
print(data["balance"])     # float
print(data["tags"])        # list[str]

# === TypedDict with total=False for all-optional ===
class PartialUpdate(TypedDict, total=False):
    user_name: str
    email: str
    is_active: bool

# === Combining Required and Optional fields ===
class BaseUser(TypedDict):
    user_id: int        # required
    user_name: str      # required

class FullUser(BaseUser, total=False):
    email: str          # optional
    is_active: bool     # optional
    tags: list[str]     # optional

# TypedDict has ZERO runtime overhead:
# no validation, no classes instantiated
# purely for static type checking with mypy/pyright

attrs: @define und cattrs fur strukturierte Daten

Die attrs-Bibliothek bietet mehr Funktionen als dataclasses, einschliesslich Validatoren und Konverter:

import attrs
from attrs import define, field, validators
import cattrs
import json

# === attrs with @define (modern API) ===
@define
class Address:
    street: str
    city: str
    zip_code: str

@define
class User:
    user_id: int
    user_name: str
    email: str = field(validator=validators.matches_re(r".+@.+\..+"))
    is_active: bool = True
    balance: float = 0.0
    tags: list[str] = field(factory=list)
    address: Address | None = None

# === cattrs for JSON structuring/unstructuring ===
converter = cattrs.Converter()

# Structure (dict -> attrs class)
raw = json.loads(json_string)
user = converter.structure(raw, User)

# Unstructure (attrs class -> dict)
data = converter.unstructure(user)
json_output = json.dumps(data)

# === Custom hooks for field name mapping ===
converter.register_structure_hook(
    User,
    cattrs.gen.make_dict_structure_fn(
        User,
        converter,
        user_id=cattrs.gen.override(rename="userId"),
        user_name=cattrs.gen.override(rename="userName"),
    )
)

# Now parses camelCase JSON keys to snake_case fields
camel_data = {"userId": 1, "userName": "Alice", "email": "a@b.com"}
user = converter.structure(camel_data, User)

# === Frozen attrs class (immutable) ===
@define(frozen=True)
class Config:
    host: str
    port: int
    debug: bool = False

Arbeiten mit verschachtelten JSON-Strukturen

Reale APIs liefern selten flaches JSON. Die meisten Antworten enthalten verschachtelte Objekte und polymorphe Typen:

Verschachtelte Modelle: Jede Verschachtelungsebene erzeugt eine separate Python-Klasse. Pydantic validiert rekursiv.

List[Model] und Optional-Felder: Ein Feld wie "items": [{"id": 1}] wird zu list[Item] in Python.

Union-Typen und diskriminierte Unions: Pydantics Discriminator bietet typsichere Deserialisierung.

# Discriminated union with Pydantic v2
from pydantic import BaseModel, Discriminator, Tag, TypeAdapter
from typing import Annotated, Literal, Union


class EmailNotification(BaseModel):
    type: Literal["email"]
    message: str
    recipient: str
    subject: str


class SmsNotification(BaseModel):
    type: Literal["sms"]
    message: str
    phone_number: str


class PushNotification(BaseModel):
    type: Literal["push"]
    message: str
    device_token: str
    title: str


Notification = Annotated[
    Union[
        Annotated[EmailNotification, Tag("email")],
        Annotated[SmsNotification, Tag("sms")],
        Annotated[PushNotification, Tag("push")],
    ],
    Discriminator("type"),
]

# Usage:
data = {"type": "email", "message": "Hello", "recipient": "a@b.com", "subject": "Hi"}
notif = TypeAdapter(Notification).validate_python(data)
isinstance(notif, EmailNotification)  # True

# JSON input automatically resolves to the correct type
json_str = '{"type": "sms", "message": "Alert", "phone_number": "+1234567890"}'
sms = TypeAdapter(Notification).validate_json(json_str)
isinstance(sms, SmsNotification)  # True

Fortgeschrittene Muster: Pydantic Settings, JSON Schema, Validatoren und berechnete Felder

Pydantic Settings ladt Konfiguration aus JSON-Dateien, Umgebungsvariablen und .env-Dateien:

from pydantic_settings import BaseSettings, SettingsConfigDict
from pydantic import Field

class AppSettings(BaseSettings):
    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        json_file="config.json",
    )

    app_name: str = "MyApp"
    debug: bool = False
    database_url: str = Field(alias="DATABASE_URL")
    redis_url: str = "redis://localhost:6379"
    max_connections: int = 100

# Loads from config.json, .env, and environment variables
# Priority: env vars > .env file > json file > defaults
settings = AppSettings()
print(settings.database_url)
print(settings.max_connections)

JSON Schema-Generierung ist in Pydantic v2 eingebaut. Jedes BaseModel kann ein JSON Schema erzeugen:

from pydantic import BaseModel
import json

class Product(BaseModel):
    id: int
    name: str
    price: float
    tags: list[str] = []

# Generate JSON Schema
schema = Product.model_json_schema()
print(json.dumps(schema, indent=2))
# Output:
# {
#   "title": "Product",
#   "type": "object",
#   "properties": {
#     "id": {"title": "Id", "type": "integer"},
#     "name": {"title": "Name", "type": "string"},
#     "price": {"title": "Price", "type": "number"},
#     "tags": {"title": "Tags", "type": "array",
#              "items": {"type": "string"}, "default": []}
#   },
#   "required": ["id", "name", "price"]
# }

# Use schema for API docs, validation, or code generation
# FastAPI uses this automatically for OpenAPI documentation

Benutzerdefinierte Validatoren und Serialisierungs-Aliase ermoglichen Datentransformation und Feldnamen-Kontrolle:

from pydantic import BaseModel, Field, field_validator, computed_field
from datetime import datetime

class User(BaseModel):
    first_name: str = Field(alias="firstName")
    last_name: str = Field(alias="lastName")
    email: str
    birth_date: datetime = Field(alias="birthDate")

    @field_validator("email")
    @classmethod
    def validate_email(cls, v: str) -> str:
        if "@" not in v:
            raise ValueError("Invalid email address")
        return v.lower().strip()

    @computed_field
    @property
    def full_name(self) -> str:
        return f"{self.first_name} {self.last_name}"

    @computed_field
    @property
    def age(self) -> int:
        today = datetime.now()
        return today.year - self.birth_date.year

# Parse from JSON with camelCase keys
user = User.model_validate_json(
    '{"firstName":"Alice","lastName":"Smith",'
    '"email":"ALICE@example.com","birthDate":"1990-05-15T00:00:00"}'
)
print(user.full_name)  # "Alice Smith"
print(user.email)      # "alice@example.com"
print(user.age)        # computed from birth_date

# Computed fields appear in serialized output
print(user.model_dump())
# {"first_name": "Alice", "last_name": "Smith",
#  "email": "alice@example.com", "birth_date": ...,
#  "full_name": "Alice Smith", "age": 35}

Best Practices fur JSON-zu-Python-Konvertierung

Befolgen Sie diese Best Practices fur robuste Anwendungen:

Pydantic fur API-Code: Validierung, Serialisierung und JSON Schema-Generierung inklusive.

Dataclasses fur interne Strukturen: Leichtgewichtige Container ohne Validierungs-Overhead.

Optionale Felder explizit behandeln: Optional[T] oder T | None mit Standard None.

Aliase fur Namenskonventionen: Field(alias="camelCase") in Pydantic.

Fruh validieren, schnell fehlschlagen: @field_validator in Pydantic hinzufugen.

Strikten Modus verwenden: ConfigDict(strict=True) verhindert implizite Typkonvertierung.

Typen aus JSON Schema generieren: datamodel-code-generator fur automatische Modellgenerierung.

Verwandte Tools: JSON zu Java, JSON zu TypeScript, JSON zu Dart.

JSON to JavaJSON to TypeScriptJSON to Dart

Haufig gestellte Fragen

Pydantic oder dataclasses: Was soll ich verwenden?

Verwenden Sie Pydantic fur Validierung, JSON Schema und FastAPI-Integration. Verwenden Sie dataclasses fur leichtgewichtige Container. Pydantic bietet auch einen Dataclass-Dekorator, der Validierung zu Standard-Dataclasses hinzufugt.

Wie konvertiere ich ein Python-Dict in eine Dataclass?

Pydantic: User.model_validate({"name": "Alice"}). Dataclasses: dacite.from_dict(data_class=User, data=my_dict). Einfach: User(**my_dict).

Wie behandle ich verschachteltes JSON mit optionalen Feldern?

Pydantic behandelt verschachtelte Modelle automatisch. Verwenden Sie Optional[NestedModel] = None. Pydantic validiert rekursiv. Fur dataclasses verwenden Sie dacite oder dataclasses-json.

Die Konvertierung von JSON zu Python-Dataclasses ist eine grundlegende Fahigkeit. Nutzen Sie unser kostenloses Tool fur sofortige Generierung und diesen Leitfaden fur Best Practices.

Konvertieren Sie JSON sofort in Python-Dataclasses mit unserem kostenlosen Tool.

War das hilfreich?

Stay Updated

Get weekly dev tips and new tool announcements.

No spam. Unsubscribe anytime.

Partner Picks

Sponsor this article

Place your product next to this developer topic with tracked clicks.

Ask about article sponsorship

This site uses cookies for analytics and to display ads. By continuing to browse, you agree. Privacy Policy