Pega la respuesta de una API o cualquier JSON de ejemplo y obtén sus tipos listos para copiar: interfaces de TypeScript, records o clases de C#, records de Java, structs de Go y de Rust, y dataclasses o TypedDict de Python. La herramienta recorre todo el ejemplo: si un campo falta en algún elemento de una lista lo marca como opcional, si vale null lo marca como anulable, y reutiliza el mismo tipo cuando dos objetos tienen la misma forma. Todo se calcula en tu navegador: el JSON no se envía a ningún lado.
Un ejemplo no es un contrato. La herramienta deduce los tipos de lo que ve: si en tu ejemplo un precio siempre es entero, saldrá como entero, y si una lista llega vacía no puede saber qué contiene. Usa un ejemplo lo más completo posible, con varios elementos en las listas, y revisa los avisos. Si la API tiene un esquema oficial (OpenAPI o JSON Schema), genera los tipos a partir de él.
Cómo deduce los tipos
- Números: si todos los valores de un campo son enteros, el tipo es entero (
int, olongeint64si alguno no cabe en 32 bits); si alguno tiene decimales, esdoubleofloat64. En TypeScript todo esnumbery en Rust se usani64yf64. - Listas de objetos: se juntan los campos de todos los elementos. Un campo que aparece en unos sí y en otros no es opcional:
campo?en TypeScript, un tipo con?en C#, un puntero conomitemptyen Go,Optionen Rust o= Noneen Python. - null: un campo que vale null en algún elemento es anulable. Si en el ejemplo solo vale null, no hay forma de saber su tipo, y sale como genérico (
unknown,object?,anyoserde_json::Value). - Objetos anidados: cada uno es un tipo con el nombre de su clave en PascalCase; los de una lista toman el singular (
pedidos→Pedido,ciudades→Ciudad). Si dos objetos tienen exactamente los mismos campos, se usa un único tipo. - Nombres: cada lenguaje recibe su estilo (
nombreCompletoen Java,NombreCompletoen C# y Go,nombre_completoen Python y Rust) y la clave original se conserva con[JsonPropertyName],@JsonProperty, la etiquetajson:"…"de Go o#[serde(rename)]. Los acentos se quitan, la ñ pasa a ni (año→anio) y las palabras reservadas se escapan (type→r#typeen Rust,class_en Java y Python,@classen C#).
Así queda el mismo campo opcional y anulable en cada lenguaje:
TypeScript cupon?: string | null;
C# [property: JsonPropertyName("cupon")] string? Cupon
Java @JsonProperty("cupon") String cupon
Go Cupon *string `json:"cupon,omitempty"`
Python cupon: str | None = None
Rust #[serde(default, skip_serializing_if = "Option::is_none")]
pub cupon: Option<String>,Qué necesita cada lenguaje
- TypeScript: nada; las interfaces solo existen al compilar. Si quieres validar en ejecución que el JSON cumple el tipo, usa una biblioteca como Zod. Más en nuestra sección de TypeScript.
- C#:
System.Text.Json, incluido en .NET. Los record posicionales necesitan C# 9; la clase con propiedades funciona en cualquier versión. Más en la sección de C#. - Java: Jackson 2.12 o posterior, que entiende los record de Java 16. Cada record público va en su propio archivo; los demás pueden quedarse juntos sin
public. Más en la sección de Java. - Go: solo
encoding/json. Los punteros distinguen un campo que falta o es null de uno que vale cero o está vacío. Más en la sección de Go. - Python: las dataclasses traen
from_dictyto_dictpara pasar de y a JSON con las claves originales; los TypedDict solo los usa el comprobador de tipos. Más en la sección de Python. - Rust:
serdecon la funciónderiveyserde_json. Más en la sección de Rust.
Leer JSON con tipos en Python
Una dataclass como las que genera la herramienta, usada con el módulo json de la biblioteca estándar:
from __future__ import annotations
import json
from dataclasses import dataclass, asdict
from typing import Any
# Lo que genera la herramienta para {"id": 10, "total": 299.9, "cupon": "OTONO"}, si "cupon" falta en algún pedido
@dataclass
class Pedido:
id: int
total: float
cupon: str | None = None # opcional: va al final y con valor por defecto
@classmethod
def from_dict(cls, d: dict[str, Any]) -> Pedido:
return cls(id=d["id"], total=float(d["total"]), cupon=d.get("cupon"))
texto = '[{"id": 10, "total": 299.9, "cupon": "OTONO"}, {"id": 11, "total": 50}]'
pedidos = [Pedido.from_dict(p) for p in json.loads(texto)]
print(pedidos[1]) # el 50 llega como int y float() lo deja en 50.0
print(sum(p.total for p in pedidos))
print(json.dumps([asdict(p) for p in pedidos])) # asdict sirve si los nombres coinciden con las claves
try:
Pedido.from_dict({"total": 5})
except KeyError as falta: # un campo obligatorio que no viene
print("Falta el campo", falta)$ python3 json_a_dataclass.pyPedido(id=11, total=50.0, cupon=None)
349.9
[{"id": 10, "total": 299.9, "cupon": "OTONO"}, {"id": 11, "total": 50.0, "cupon": null}]
Falta el campo 'id'Otras herramientas para desarrolladores: formateador de JSON, conversor entre JSON, YAML, TOML y CSV, decodificador de JWT y generador de UUID. Todas, en decodigo.com/herramientas.
Antes de publicarla, el código que genera para 60 JSON de ejemplo (respuestas de API, GeoJSON, claves con espacios, acentos, palabras reservadas, listas vacías, valores mixtos y null) se compiló con TypeScript 5.6 en modo --strict, .NET 8 con los avisos tratados como errores, Java 21 con Jackson 2.18, Go 1.23, Rust 1.82 con serde y mypy en modo estricto. En los seis lenguajes, cada ejemplo se lee con los tipos generados y se vuelve a escribir igual que el original.