DevToolBoxKOSTENLOS
Blog

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.

𝕏 Twitterin LinkedIn
War das hilfreich?

Bleiben Sie informiert

Wöchentliche Dev-Tipps und neue Tools.

Kein Spam. Jederzeit abbestellbar.

Verwandte Tools ausprobieren

PYJSON to PythonTSJSON to TypeScript{ }JSON Formatter

Verwandte Artikel

JSON zu Java-Klasse Konverter: POJO, Jackson, Gson & Lombok Anleitung

JSON in Java-Klasse online konvertieren. POJO aus JSON mit Jackson, Gson und Lombok generieren mit Codebeispielen.

JSON zu TypeScript: VollstÀndiger Leitfaden mit Beispielen

Erfahren Sie, wie Sie JSON-Daten automatisch in TypeScript-Interfaces konvertieren. Verschachtelte Objekte, Arrays, optionale Felder und Best Practices.

JSON zu Go Struct: Mapping-Strategien und Best Practices

Meistern Sie die Konvertierung von JSON zu Go-Structs. Struct-Tags, verschachtelte Typen, omitempty, benutzerdefiniertes Marshaling und Praxismuster.