Problema: Desajuste entre nombres de claves JSON y propiedades Java
En muchas ocasiones, los nombres de las claves en los datos JSON que recebimos no coinciden con los nombres de los atributos de nuestros objetos Java. Esto provoca que los valores no se mapean correctamente y queden como null en nuestro bean.
Escenario inicial
Supongamos que tenemos la siguiente clase Java para recibir datos:
package com.ejemplo.modelo;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class Persona {
private String nombreCompleto;
private String edad;
private String profesion;
public String getNombreCompleto() {
return nombreCompleto;
}
public void setNombreCompleto(String nombreCompleto) {
this.nombreCompleto = nombreCompleto;
}
public String getEdad() {
return edad;
}
public void setEdad(String edad) {
this.edad = edad;
}
public String getProfesion() {
return profesion;
}
public void setProfesion(String profesion) {
this.profesion = profesion;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
Y recebimos un JSON con claves en formato snake_case:
{"nombre_completo": "Juan Pérez", "edad": "30", "profesion": "Ingeniero"}
Al procesar este JSON, todas las propiedades quedan en null:
Persona[nombreCompleto=null, edad=null, profesion=null]
Solución con @JsonProperty (Jackson)
Cuando se utiliza Jackson para el procesamiento de JSON, podemos utilizar la anotación @JsonProperty en los seters para indicar el nombre de la clave JSON que debe mapearse a cada propiedad.
package com.ejemplo.modelo;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class Persona {
private String nombreCompleto;
private String edad;
private String profesion;
public String getNombreCompleto() {
return nombreCompleto;
}
@JsonProperty(value = "nombre_completo")
public void setNombreCompleto(String nombreCompleto) {
this.nombreCompleto = nombreCompleto;
}
public String getEdad() {
return edad;
}
@JsonProperty(value = "edad")
public void setEdad(String edad) {
this.edad = edad;
}
public String getProfesion() {
return profesion;
}
public void setProfesion(String profesion) {
this.profesion = profesion;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
Ahora el mapeo funciona correctmaente:
Persona[nombreCompleto=Juan Pérez, edad=30, profesion=Ingeniero]
Solución con @JSONField (FastJSON)
Si se utiliza FastJSON en lugar de Jackson, la anotación tiene un nombre diferente: @JSONField. Esta librería ofrece una sintaxis similar pero con algunas diferencias.
Mapeo de claves JSON
La propiedad name permite especificar el nombre de la clave JSON que corresponde a cada campo:
package com.ejemplo.modelo;
import com.alibaba.fastjson.annotation.JSONField;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class Persona {
private String nombreCompleto;
private String edad;
private String profesion;
public String getNombreCompleto() {
return nombreCompleto;
}
@JSONField(name = "nombre_completo")
public void setNombreCompleto(String nombreCompleto) {
this.nombreCompleto = nombreCompleto;
}
public String getEdad() {
return edad;
}
@JSONField(name = "edad")
public void setEdad(String edad) {
this.edad = edad;
}
public String getProfesion() {
return profesion;
}
public void setProfesion(String profesion) {
this.profesion = profesion;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
Con esta configuración, FastJSON puede deserialize correctamente el JSON entrante.
Exclusión de campos en la serialización
La propiedad serialize = false permiteexclude un campo de la serialización JSON. Esto es útil cuando no queremos incluir ciertos atributos en la respuesta JSON:
package com.ejemplo.modelo;
import com.alibaba.fastjson.annotation.JSONField;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class Persona {
private String nombreCompleto;
private String edad;
private String profesion;
private String contrasena;
public String getNombreCompleto() {
return nombreCompleto;
}
@JSONField(name = "nombre_completo")
public void setNombreCompleto(String nombreCompleto) {
this.nombreCompleto = nombreCompleto;
}
public String getEdad() {
return edad;
}
@JSONField(name = "edad")
public void setEdad(String edad) {
this.edad = edad;
}
public String getProfesion() {
return profesion;
}
public void setProfesion(String profesion) {
this.profesion = profesion;
}
public String getContrasena() {
return contrasena;
}
@JSONField(serialize = false)
public void setContrasena(String contrasena) {
this.contrasena = contrasena;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
}
}
Al serializar un objeto Persona, el campo contrasena no aparecerá en el JSON resultante, lo cual es ideal para proteger información sensible.
Resumen de propiedades
Las propiedades más utilizadas de @JSONField incluyen:
name: Especifica el nombre de la clave JSONserialize: Controla si el campo se incluye en la serialización (por defecto true)deserialize: Controla si el campo se incluye en la deserialización
Estas anotaciones facilitan enormemente el trabajo con APIs que utilizan convenciones de nomenclatura diferentes a las estándar en Java.