Skip to content
Voltar ao blog
Tutoriais

JSON para Python: dataclass, Pydantic e TypedDict (guia 2026)

Converta JSON em classes Python do jeito certo: dataclass, Pydantic v2 ou TypedDict, tipagem Optional, alias camelCase e ciladas a evitar. Grátis, online.

12 min de leitura

JSON para Python: dataclass, Pydantic e TypedDict (guia 2026)

Cole seu JSON no conversor de JSON para Python e copie as classes que ele gera. Esse é o caminho rápido para transformar JSON em Python: nada para instalar, nada enviado a servidores, tudo roda no seu navegador. Para um modelo pontual ou uma olhada rápida na resposta de uma API que você não conhece, você termina em segundos.

O problema mais difícil é aquele que um conversor não resolve por você. Ele infere os tipos corretos, mas não toma a decisão que mais importa: qual das três saídas você realmente quer. Um dataclass, um BaseModel do Pydantic v2 e um TypedDict conseguem descrever exatamente o mesmo JSON, mas se diferenciam na força da segurança de tipos, no comportamento em tempo de execução e na forma como você carrega dados neles. Escolha errado e ou você carrega a dependência do Pydantic à toa, ou parte do princípio de que um dataclass valida sua entrada quando, na surdina, ele não valida.

Este guia cobre a matriz de decisão para os três, como o conversor infere tipos, o tratamento de camelCase em cada modo, como fazer o parsing de JSON com a classe que você gera (incluindo a armadilha dos aninhados que pega muita gente), Pydantic v2 versus v1 e os casos-limite que vale conhecer antes de confiar na saída.

Como converter JSON para Python

Converter JSON para Python leva três passos:

  1. Cole seu JSON. Jogue um objeto, um array ou a resposta bruta de uma API na caixa de entrada. A conversão roda na hora e totalmente no lado do cliente.
  2. Escolha dataclass, Pydantic ou TypedDict. Use o seletor de saída para alternar entre os estilos e renomeie o Root padrão para algo com significado, como User ou ApiResponse.
  3. Copie ou baixe. Pegue o Python gerado com um clique e solte o models.py direto no seu projeto.

Esse é todo o caminho prático. Se sua entrada estiver minificada ou você não tiver certeza de que é válida, passe-a antes por um formatador de JSON para que o conversor tenha um JSON limpo e bem formado para ler. O resto deste guia explica a saída para você corrigir os casos que uma ferramenta não consegue inferir sozinha.

Como os tipos JSON mapeiam para Python

Todo valor JSON tem um equivalente em Python, e o mapeamento é direto:

Valor JSONTipo Python
"text"str
42int (precisão arbitrária, sem overflow)
3.14, 2e3float
true / falsebool
nullOptional[T] / Optional[Any]
[1, 2, 3]List[T]
{ ... }uma classe nomeada

Veja um payload REST típico:

{ "id": 101, "name": "Ada Lovelace", "email": "ada@example.com", "active": true, "roles": ["admin", "user"] }

No modo dataclass, o conversor produz uma classe pronta para uso:

from dataclasses import dataclass
from typing import List


@dataclass
class Root:
    id: int
    name: str
    email: str
    active: bool
    roles: List[str]

Uma sutileza distingue o Python. Seu int tem precisão arbitrária, então um snowflake do Discord ou qualquer ID acima de 2^53 continua sendo um int comum, sem perda. O JavaScript perderia precisão no mesmo valor, e o Rust faria você escolher entre i64, u64 e f64. Só um token escrito com ponto decimal ou expoente é inferido como float. Um campo que é sempre null não pode ter o tipo definido só a partir da amostra, então ele recai para Optional[Any]; cole um payload representativo com um valor preenchido para obter algo mais preciso que Any.

dataclass vs Pydantic v2 vs TypedDict: qual você deve usar?

Essa é a escolha que o conversor devolve para você, e é a razão de ser deste guia. A versão curta: use um dataclass quando quiser um contêiner de dados interno sem dependências, Pydantic v2 quando fizer parsing ou validação de entrada não confiável, e TypedDict quando só precisar de dicas de tipo estáticas sobre dicts que você já tem.

DimensãodataclassPydantic v2TypedDict
DependênciaBiblioteca padrãopip install pydanticBiblioteca padrão (typing)
Validação em runtimeNãoSim: valida e faz coerçãoNão: só dicas
Custo em runtimeLeve (objeto real)Algum (validação)Zero (é um dict)
Parsing automático de aninhadosNão, manualSim, model_validate é recursivoNão, é um dict
Aliases camelCaseMantém a chave originalField(alias=...)Sintaxe funcional
Melhor paraEstruturas internasRespostas de API, webhooks, configTipar código legado com dict

Os três destinos ficam quase idênticos para uma estrutura pequena como {"id": 101, "name": "Ada Lovelace", "active": true}. Um dataclass Python a partir de JSON dá a você um objeto da biblioteca padrão:

from dataclasses import dataclass


@dataclass
class Root:
    id: int
    name: str
    active: bool

A versão em Pydantic troca por BaseModel, que parece semelhante mas justifica seu peso em tempo de execução:

from pydantic import BaseModel


class Root(BaseModel):
    id: int
    name: str
    active: bool

E um TypedDict Python a partir de JSON descreve o formato de um dict comum sem alocar nada novo:

from typing import TypedDict


class Root(TypedDict):
    id: int
    name: str
    active: bool

Os mesmos campos, três contratos bem diferentes. O valor de gerar os três a partir de uma amostra é que você pode alternar entre eles na ferramenta e casar o modo com a tarefa: Pydantic quando você faz parsing de dados que não controla, um dataclass quando os dados são internos, e TypedDict quando você só quer que o mypy entenda um dict que já circula pelo seu código.

Como o conversor infere as classes

Três regras cobrem quase tudo o que você fornece: uma classe por formato de objeto, mesclagem chave a chave para arrays e tratamento cuidadoso de valores ausentes.

Inferência estrutural: uma classe nomeada por objeto

Cada formato distinto de objeto vira uma classe nomeada própria. Objetos aninhados não são embutidos; eles são içados para definições separadas e referenciadas:

{ "repo": "pydantic", "owner": { "login": "samuelcolvin", "id": 100 } }
from dataclasses import dataclass


@dataclass
class Owner:
    login: str
    id: int


@dataclass
class Root:
    repo: str
    owner: Owner

O owner aninhado vira sua própria classe Owner, referenciada pelo campo. Repare na ordem: a classe filha é emitida antes da classe pai que a usa, então o módulo roda sem from __future__ import annotations. Formatos idênticos também são deduplicados, então dois campos com a mesma estrutura compartilham uma única classe em vez de gerar cópias.

Mesclagem de arrays e campos Optional

Quando você passa um array de objetos, o conversor mescla-os chave a chave em um único tipo de elemento. Uma chave presente em alguns elementos, mas ausente em outros, vira Optional:

{ "users": [{ "id": 1, "nick": "x" }, { "id": 2 }] }
from typing import List, Optional, TypedDict


class User(TypedDict):
    id: int
    nick: Optional[str]


class Root(TypedDict):
    users: List[User]

id aparece em todos os elementos, então continua obrigatório. nick está ausente no segundo usuário, então vira Optional[str]. É exatamente por isso que um único objeto é uma amostra fraca: a ferramenta só marca um campo como Optional quando de fato vê a chave faltando em algum lugar. Forneça um array representativo e a inferência refletirá o formato real.

Optional, None e arrays vazios

Alguns valores não carregam tipo por conta própria, e o conversor é honesto quanto a isso. Uma chave ausente em alguns itens do array vira Optional[T]. Um campo que é sempre null vira Optional[Any], porque o null do JSON sozinho não diz nada sobre o tipo pretendido. Um array vazio ou com tipos de elemento misturados recai para List[Any]. Nada disso é bug; é a ferramenta se recusando a adivinhar. Troque cada Any por um tipo concreto assim que tiver uma amostra que mostre um, e lembre que o int de precisão arbitrária do Python significa que um ID grande nunca força você a um tipo numérico mais largo, como aconteceria em Rust ou JavaScript.

Tratando chaves camelCase em cada modo

Chaves JSON costumam ser camelCase, enquanto campos idiomáticos em Python são snake_case. Não existe uma resposta única aqui, então cada modo resolve isso de um jeito, e vale entender a diferença antes de copiar a saída.

dataclass. Um dataclass não tem mecanismo de alias embutido. Para manter Root(**data) funcionando, o conversor preserva a chave original como nome do campo sempre que ela é um identificador Python válido, então publicRepos continua publicRepos. Forçá-la para snake_case quebraria o **data com um TypeError.

Pydantic v2. Aqui você tem o resultado idiomático. Os campos são renomeados para snake_case com um Field(alias=...) que mapeia de volta para a chave JSON exata, mais model_config = ConfigDict(populate_by_name=True) para você construir o modelo pelo nome do campo ou pelo alias:

from pydantic import BaseModel, ConfigDict, Field


class Root(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    login: str
    public_repos: int = Field(alias="publicRepos")
    created_at: str = Field(alias="createdAt")

É isso que permite ao Root.model_validate(api_json) fazer o round-trip de payloads camelCase reais enquanto seu código continua legível.

TypedDict. Chaves que são identificadores simples são usadas diretamente. Assim que surge uma chave que não é um identificador, o conversor passa para a sintaxe funcional TypedDict('User', {...}) para preservar a chave exata, o que a próxima seção aborda.

Palavras-chave do Python e chaves que não são identificadores

Chaves JSON são apenas strings, então nada impede uma API de enviar class, from ou first-name. Cada uma seria um erro de sintaxe como atributo Python puro, então o conversor as sanitiza.

Uma chave que é palavra-chave do Python ganha um underscore no final: class vira class_, from vira from_. No modo Pydantic, ela também mantém um Field(alias=...) para que o modelo continue lendo a chave original do wire:

from pydantic import BaseModel, ConfigDict, Field


class Root(BaseModel):
    model_config = ConfigDict(populate_by_name=True)

    class_: str = Field(alias="class")
    from_: str = Field(alias="from")

Chaves com hífens, espaços ou dígito no início são sanitizadas para identificadores válidos nos modos dataclass e Pydantic. TypedDict é o único modo que consegue manter a chave literal, usando a forma funcional para que "first-name" sobreviva intacta:

from typing import TypedDict

Root = TypedDict("Root", {"first-name": str, "2fa": bool})

O resultado sempre roda, e no modo TypedDict a chave permanece byte a byte igual ao que a API enviou.

Parsing de JSON com a classe gerada

Gerar a classe é metade do trabalho. Carregar JSON nela é onde os três modos divergem bastante, e onde uma suposição comum dá errado.

dataclass. Para um objeto plano, json.loads mais Root(**data) funciona direto:

import json
from dataclasses import dataclass
from typing import List


@dataclass
class Root:
    id: int
    name: str
    roles: List[str]


data = json.loads('{"id": 101, "name": "Ada Lovelace", "roles": ["admin"]}')
root = Root(**data)
print(root.name, root.roles)

Mas um dataclass não é recursivo. Se o JSON tem objetos aninhados, Root(**data) preenche o nível superior e deixa cada filho como um dict comum, não como a classe Owner que você gerou. Para fazer o parsing de uma árvore inteira, construa os filhos você mesmo, use uma biblioteca como o dacite ou pydantic.dataclasses, ou troque a ferramenta para o modo Pydantic. Essa é a cilada que a maioria dos conversores nunca menciona.

Pydantic v2. Uma única chamada faz o parsing e valida a árvore inteira:

import json
from pydantic import BaseModel


class Root(BaseModel):
    id: int
    name: str


root = Root.model_validate(json.loads('{"id": 101, "name": "Ada Lovelace"}'))
print(root.name)

Uma incompatibilidade de tipo levanta um ValidationError claro em vez de passar despercebida. Esse é o retorno pela dependência: o modelo é um validador em tempo de execução, então uma chamada de model_validate que retorna é a garantia de que os dados bateram, não apenas a crença de um verificador de tipos de que bateram.

TypedDict. Em tempo de execução, um TypedDict é um dict comum. Anotar data: Root = json.loads(text) serve puramente para o verificador estático; nada é validado quando o código roda. Se você precisa de imposição real, rode o mypy no CI ou migre para o Pydantic. Escolher um modo é, na prática, escolher quanta garantia em runtime você quer, e quando um contrato precisa ir além dos tipos, o guia de validação com JSON Schema cobre a imposição de estrutura de ponta a ponta.

Pydantic v2 vs v1: o que mudou

O conversor emite sintaxe v2. Muitas bases de código ainda rodam v1, e colar a saída v2 em um projeto v1 falha por causa de métodos renomeados, não por lógica. Aqui está o mapeamento:

TarefaPydantic v2 (saída da ferramenta)Pydantic v1
Fazer parsing de um dictmodel_validate(data)parse_obj(data)
Fazer parsing de JSON brutomodel_validate_json(text)parse_raw(text)
Configurar o modelomodel_config = ConfigDict(...)class Config interna
Serializar para dictmodel_dump()dict()
Serializar para JSONmodel_dump_json()json()

O comportamento dos aliases também ficou mais rígido: no v2 você precisa de populate_by_name=True junto de Field(alias=...) para construir um modelo pelo nome do campo ou pelo alias, e é por isso que a ferramenta o inclui. Na prática, migre para o v2 se puder, já que ele é mais rápido e o foco do desenvolvimento atual; caso contrário, reescreva model_validate de volta para parse_obj e ConfigDict de volta para uma Config interna.

json-to-python vs quicktype vs datamodel-code-generator vs manual

Não existe uma única melhor forma de gerar uma classe Python a partir de JSON. Depende de onde o JSON vive e do que você tem para começar.

AbordagemMelhor paraObservação
Conversor online (esta ferramenta, jsonlint, codeshack)Conversões pontuais, payloads sensíveis, zero instalaçãoInfere a partir de uma amostra, totalmente no lado do cliente
quicktypeSaída multilíngue, codegen em pipelineTambém guiado por amostra; o suporte a Python é mais raso em aliases e escolha de modo
datamodel-code-generatorGerar Pydantic a partir de um JSON Schema ou OpenAPIA entrada é um schema, não uma amostra. É mais autoritativo quando você tem um
Escrever classes à mãoPayloads minúsculos, aprender a sintaxeControle total, mas tedioso e fácil de sair de sincronia

A diferença central é a fonte de dados: um conversor online e o quicktype inferem tipos a partir de uma amostra de JSON, enquanto o datamodel-code-generator lê um JSON Schema ou uma spec OpenAPI como fonte da verdade. Use as ferramentas baseadas em amostra para exploração e casos pontuais; recorra à baseada em schema quando um contrato já existe. Se sua base de código é TypeScript em vez de Python, a mesma abordagem por amostra vale com o conversor de JSON para TypeScript, e o guia de interfaces JSON para TypeScript cobre esse lado em profundidade. Para o ângulo de validação em runtime em uma linguagem compilada e fortemente tipada, o guia de structs JSON para Rust faz um contraste útil: o serde é o Pydantic do Rust.

Ciladas comuns ao gerar Python a partir de JSON

Classes geradas são um ponto de partida. Fique atento a estas antes de confiar na saída com dados reais.

  • Uma amostra raramente revela todos os formatos. Optional só pode ser inferido a partir de elementos de array que de fato diferem. Cole um array representativo para que a opcionalidade seja precisa, não um chute a partir de um objeto sortudo.
  • Um dataclass não valida. Root(**data) só atribui campos; não checa tipo nenhum e não é recursivo. Se você precisa de imposição, use Pydantic.
  • JSON aninhado quebra o Root(**data). Só o nível superior é preenchido; os filhos continuam dicts. Troque para o model_validate do Pydantic, ou use o dacite.
  • Um campo sempre null vira Optional[Any]. Trate-o como um placeholder, não como um opcional normal, e dê a ele um tipo real assim que souber qual.
  • Saída v2 em um projeto v1. model_validate ficará indefinido. Migre para o v2 ou reescreva como parse_obj.
  • Uma data deixada como str não faz aritmética de datas. Troque o campo para datetime, ou no Pydantic use um campo datetime para que ele faça o parsing de ISO 8601 por você.

Perguntas frequentes

Devo usar dataclass, Pydantic ou TypedDict para JSON?

Use um dataclass para um contêiner de dados interno sem dependências. Use Pydantic v2 quando fizer parsing ou validação de entrada não confiável, porque ele impõe tipos em tempo de execução. Use TypedDict para adicionar dicas estáticas a um dict que você já tem, sem custo em runtime. Quando os dados vêm de fora do seu controle, opte por Pydantic por padrão.

O Pydantic valida JSON em tempo de execução?

Sim. Root.model_validate(data) valida e faz coerção de tipos enquanto o código roda, levantando um ValidationError quando a entrada é inválida. Um dataclass não faz nenhum dos dois: Root(**data) só atribui campos. Um TypedDict também não faz nenhum dos dois; é uma dica para verificadores estáticos como o mypy e se comporta como um dict comum em tempo de execução.

Como tipar um campo que o JSON às vezes omite?

Tipe-o como Optional[T]. Quando uma chave aparece em apenas alguns elementos do array, o conversor a marca como Optional automaticamente. No Pydantic, dê ao campo um valor padrão como = None para que uma chave ausente não dispare um erro de validação. Um campo de dataclass pode ter padrão da mesma forma com field(default=None).

Por que meu dataclass falhou ao fazer parsing de JSON aninhado?

Porque Root(**data) não é recursivo. Ele preenche os campos do nível superior e deixa os objetos aninhados como dicts comuns, em vez das classes filhas que você gerou. Para fazer o parsing da árvore inteira em uma chamada, troque para o model_validate do Pydantic, ou adicione recursão com o dacite ou pydantic.dataclasses.

Qual tipo Python um inteiro JSON grande se torna?

int. Inteiros em Python têm precisão arbitrária, então um snowflake ou ID do Discord acima de 2^53 continua um int comum, sem perda. Você nunca sofre a perda de precisão que o JavaScript tem, e nunca precisa escolher entre i64 e u64 como o Rust obriga.

Como as chaves JSON em camelCase são tratadas em cada modo?

Um dataclass mantém a chave original para que Root(**data) continue funcionando. O Pydantic renomeia os campos para snake_case com Field(alias="camelKey") mais populate_by_name=True, para fazer o round-trip de dados reais de API. O TypedDict usa a sintaxe funcional TypedDict('Name', {...}) para preservar qualquer chave que não seja um identificador válido.

Como lidar com uma chave JSON que é palavra-chave do Python, como class?

O conversor acrescenta um underscore no final, transformando class em class_ para que o código seja válido. No modo Pydantic, ele também adiciona Field(alias="class") mapeando o campo de volta para a chave original do wire, então o nome é legal e o modelo continua lendo o payload real.

Meu JSON fica privado ao usar um conversor de JSON para Python online?

Sim. A conversão roda 100% no seu navegador com JavaScript. Seu JSON (incluindo tokens, IDs e dados de clientes) nunca sai da página e nunca é enviado a um servidor. Ele funciona até offline depois que a página carrega. Quando estiver pronto, experimente o conversor de JSON para Python no seu próprio payload.

Tags: python json pydantic type-safety developer-tools

Artigos relacionados

Ver todos os artigos