Convertir JSON a Java
JSON a Java genera records o POJOs a partir de un documento de ejemplo, en tu navegador. Los campos numéricos opcionales van encapsulados — Integer en lugar de int — porque un primitivo no puede contener null, así que una clave ausente se deserializa a 0 y se vuelve indistinguible de un cero real.
El documento termina antes de tiempo; parece incompleto.
^
¿Cómo funciona Convertir JSON a Java?
Los tipos primitivos de Java hacen que la cuestión de los campos opcionales sea concreta y no meramente estética, y equivocarse produce un bug que de verdad cuesta encontrar.
Un primitivo no puede estar ausente
int count deserializa una clave count ausente a 0. Sin excepción, sin aviso — y aguas abajo no hay forma de distinguir un registro al que nunca se envió el count de uno en el que el count era realmente cero. Lo mismo aplica a boolean, que se convierte en false, y a double, que se convierte en 0.0. Por eso los campos opcionales y que admiten null van encapsulados: Integer, Long, Double, Boolean, todos los cuales pueden contener null y te permiten hacerse la pregunta.
Los genéricos no pueden contener primitivos en absoluto
List<int> no compila. Cada elemento de una lista va encapsulado sin importar la opcionalidad, razón por la que un array de enteros se convierte en List<Integer>.
record o POJO
Un record es la mejor opción por defecto en Java 17 y posteriores: inmutable, sin boilerplate, equals y hashCode y toString gratis, y Jackson los soporta desde la 2.12. Un POJO con campos privados y getters y setters es lo que querrá una base de código en Java 8 o un framework que exija un constructor sin argumentos, así que ambos están disponibles.
Las anotaciones de Jackson llevan la clave original
La convención de Java es camelCase ; las claves JSON frecuentemente no lo son. En lugar de depender de que un PropertyNamingStrategy esté configurado en otro sitio, cada componente recibe un @JsonProperty explícito con la clave exacta del documento. Así la clase se vincula correctamente sea cual sea la configuración del ObjectMapper.
Un tipo público por archivo
Java permite exactamente un tipo público de nivel superior por archivo fuente, así que un documento que produce varios tipos no compila si lo pegas de una sola vez. La salida lo indica en un comentario de cabecera: guarda cada tipo como <TypeName>.java, o anídalos dentro de una sola clase externa. Emitir un archivo que javac rechaza sin mencionarlo es el tipo de pequeña deshonestidad que hace perder diez minutos.
Ejemplo
[
{ "user_id": 1, "retries": 0, "tags": ["a"] },
{ "user_id": 2, "tags": [] }
]Generado
public record User(
@JsonProperty("user_id") int userId,
@JsonProperty("retries") Integer retries,
@JsonProperty("tags") List<String> tags
) {}¿Qué opciones y casos límite admite Convertir JSON a Java?
| Parámetro | Tipo | Valor predeterminado | Comportamiento y casos límite |
|---|---|---|---|
| Forma | record / POJO | record | record en Java 17+ para un DTO inmutable sin código repetitivo. POJO para Java 8, o para cualquier cosa que necesite un constructor sin argumentos y setters. |
| Paquete | identificador | (ninguno) | Agrega una declaración de paquete. Déjalo vacío para un archivo de prueba; establécelo para que coincida con el directorio donde residirá el archivo. |
| Jackson | booleano | on | Emite @JsonProperty con la clave JSON exacta, por lo que el enlace no depende de una estrategia de nomenclatura de ObjectMapper configurada en otro lugar. |
| Encapsular Optional | booleano | on | Un int opcional se convierte en Integer. Desactivar esto hace que una clave que falta se deserialice en 0, lo que es indistinguible de un cero real. |
| string | tipo de Java | String | Los formatos detectados no se mapean a Instant, LocalDate o UUID. Jackson necesita que JavaTimeModule esté registrado para los dos primeros, y lanza un error en cualquier registro cuyo formato difiera. |
| integer | tipo de Java | int / long | long cuando cualquier muestra supera 2,147,483,647. Un número JSON no lleva un ancho declarado, por lo que el ancho se lee a partir de los valores. |
| array | tipo de Java | List<T> | Los elementos siempre se envuelven, porque los genéricos no pueden contener primitivos. Un array mixto se convierte en List<Object>. |
| Objeto anidado | tipo | tipo propio | Promovido a su propio registro o clase, declarado antes del tipo que lo utiliza. Las formas idénticas comparten un tipo en lugar de generar duplicados numerados. |
Preguntas frecuentes
¿Por qué un campo es int y otro Integer?
El Integer aparecía en algunos registros y en otros no. Un int primitivo no puede contener null, así que Jackson deserializa una clave ausente a 0 — y nada aguas abajo puede distinguirlo de un registro que de verdad envió cero. Integer puede ser null, lo que hace visible la ausencia. Los campos presentes en todos los registros se quedan primitivos, que es más barato y más simple.
¿Puedo pegar toda la salida en un solo archivo?
No, si generó más de un tipo. Java permite exactamente un tipo público de nivel superior por archivo fuente, así que javac lo rechazará. Guarda cada tipo como <TypeName>.java, o convierte los anidados en static nested classes dentro de una clase externa. La salida te dice al principio en cuál de los dos casos estás.
¿record o POJO?
record si estás en Java 17 o posterior y el objeto son datos que llegaron de algún sitio: es inmutable, te da equals, hashCode y toString bien hechos, y Jackson soporta records desde la 2.12. POJO si estás en Java 8, si un framework exige un constructor sin argumentos y setters (JPA antiguo, algunas configuraciones de Spring), o si el objeto de verdad necesita mutarse después de construirse.
¿Por qué mi marca de tiempo es un String y no un Instant?
Porque Jackson no puede interpretarlo como Instant sin JavaTimeModule registrado, y porque lanza una excepción con el primer registro cuyo formato difiere de lo que espera. Las API reales envían segundos Unix, milisegundos Unix, ISO 8601 con y sin offset, y ocasionalmente algo regional. El formato detectado se indica en la referencia de arriba para que puedas cambiar el tipo a conciencia y añadir un convertidor si el formato lo necesita.
¿Necesito Lombok?
No, y el generador deliberadamente no emite @Data ni @Builder. En Java 17+ un record cubre para lo que estaba el @Value de Lombok, sin un procesador de anotaciones en tu build. La salida POJO es Java llano con getters y setters reales, que compila en cualquier sitio. Si tu proyecto ya usa Lombok puedes reemplazar los accessors tú mismo, pero aquí nada lo requiere.
¿Se sube mi JSON?
No. El análisis, la inferencia de tipos y el renderizado se ejecutan todos en tu navegador — abre el panel Network mientras pegas y no verás salir nada. Los ejemplos que la gente pega en un generador de clases suelen ser respuestas reales de un sistema con el que se están integrando, que es justo el material que no debería pasar por un servidor desconocido.