JSON a Rust
Genera structs de Rust con derives Deserialize de serde a partir de una muestra JSON.
Entrada JSON
Código generado
Escribe JSON a la izquierda para generar un tipo a partir de él.
¿Qué es JSON a Rust?
Un conversor de JSON a Rust genera las definiciones de structs necesarias para deserializar un payload JSON con serde, el crate que prácticamente todo proyecto Rust usa para esto. Rust no tiene ningún tipo de objeto dinámico al que recurrir, así que consumir una API significa declarar la forma exacta por adelantado: un struct por objeto, un tipo concreto por campo y un #[derive(Deserialize)] en cada uno para que serde genere la implementación de parseo en compilación. Hacerlo a mano para una respuesta profundamente anidada es lento y repetitivo, y el compilador rechazará todo por un único tipo que no coincida. Este generador produce cada struct en orden de dependencias con los atributos derive ya adjuntos, para que puedas pegar el resultado en un módulo y que serde_json::from_str funcione de inmediato. Los nombres de campo se convierten además al snake_case idiomático de Rust automáticamente, y siempre que eso difiera de la clave JSON original — lo que ocurrirá con casi cualquier payload camelCase — se genera un atributo `#[serde(rename = "...")]` en ese campo exacto, así que nunca tienes que añadir atributos de renombrado a mano.
Cómo usar JSON a Rust
- Pega una respuesta JSON representativa en el panel izquierdo — los campos de los structs reflejan exactamente las claves presentes en esa muestra.
- Fija un nombre de struct raíz sobre la salida (por defecto "Root"); cada objeto anidado se convierte en su propio struct con el nombre de la clave que lo contiene.
- Añade serde a Cargo.toml con la característica derive (`serde = { version = "1", features = ["derive"] }`) más serde_json, y luego pega los structs.
- Cambia el selector de lenguaje sobre la salida para generar la misma forma como un struct de Go o un tipo TypeScript.
Ejemplos
Objeto plano con un derive Deserialize
Entrada
{"id": 1, "name": "Ada", "active": true}
Salida
use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct Root { pub id: i64, pub name: String, pub active: bool, }
Un objeto anidado se convierte en su propio struct
Entrada
{"user": {"id": 1, "name": "Ada", "roles": ["admin"]}}
Salida
use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct User { pub id: i64, pub name: String, pub roles: Vec<String>, } #[derive(Debug, Deserialize)] pub struct Root { pub user: User, }
Clave camelCase renombrada automáticamente a snake_case
Entrada
{"userId": 1, "firstName": "Ada"}
Salida
use serde::Deserialize; #[derive(Debug, Deserialize)] pub struct Root { #[serde(rename = "userId")] pub user_id: i64, #[serde(rename = "firstName")] pub first_name: String, }
Errores comunes
- Olvidar la característica derive de serde en Cargo.toml, lo que produce un error de resolución de macro que no señala obviamente el feature flag ausente.
- Dejar todos los campos como no-Option cuando la API a veces omite uno — serde entonces falla toda la deserialización en tiempo de ejecución en lugar de dejar ese único campo vacío.
- Añadir tu propio atributo de contenedor #[serde(rename_all = "camelCase")] encima de los structs generados — es redundante, ya que cada campo que lo necesita lleva su propio #[serde(rename = "...")].
- Tratar como suficientes todos los campos serde_json::Value — compila, pero sigue haciendo falta match a mano para extraer un valor utilizable; es la señal de que la muestra no fijó el tipo de ese campo, no un resultado terminado.
- Marcar un campo como Option<T> solo porque JSON técnicamente permite null en cualquier sitio, y no porque la muestra realmente lo mostrara ausente o null — envolver todos los campos en Option<T> hace que un valor genuinamente ausente sea más difícil de notar después.
Por qué usar esta herramienta
- Emite los atributos #[derive(Debug, Deserialize)] y el import de serde, así la salida compila tal cual sin necesidad de añadir boilerplate.
- Declara los structs anidados en orden de dependencias, así el archivo compila de arriba abajo sin reordenar a mano.
- Se ejecuta íntegramente en el cliente, así que una respuesta usada para generar estos structs nunca abandona tu máquina antes de que una sola línea llegue a Cargo.
- Convierte cada campo al snake_case idiomático y adjunta un #[serde(rename = "...")] por campo donde difiera de la clave de origen, así un payload camelCase compila sin tocar tú mismo los atributos de nombres de serde.
- Recurre a serde_json::Value solo donde genuinamente no puede fijarse un tipo concreto — una muestra con solo null, o un array que mezcla un entero y un float para la misma clave — en lugar de adivinar en silencio el primitivo equivocado.