Deserialización JSON de tipos genéricos usando Jackson

Como parte de las buenas prácticas en la reutilización de clases, es común encontrar el uso de tipos genéricos en Java. Sin embargo, cuando estas forman parte de una respuesta JSON, pueden ser un dolor de cabeza. Imaginemos que tenemos como respuestas de un API Rest los dos siguientes escenarios:

PersonaResponse
{
   "codigo":1,
   "mensaje":"ok",
   "data":{
      "nombres":"Rolando Palermo",
      "edad":29
   }
}
ProductoResponse
{
   "codigo":1,
   "mensaje":"ok",
   "data":{
      "codigo":"FX-536",
      "precio":56.98,
      "moneda":"PEN",
   }
}
Ambos Json tienen propiedades comunes, sin embargo sólo el objeto data es diferente. Para deserializar estos jsons sin tipos genéricos, tendríamos que crear 2 envoltorios con 2 clases de datos diferentes. Pero con los tipos genéricos solo necesitamos crear una clase genérica para las propiedades de la raíz y una clase específica para cada objeto en la clave de data.

GenericResponse.java
public class GenericResponse<T> {

    private String codigo;
    private String mensaje;
    T data;

    public String getCodigo() {
        return codigo;
    }

    public void setCodigo(String codigo) {
        this.codigo = codigo;
    }

    public String getMensaje() {
        return mensaje;
    }

    public void setMensaje(String mensaje) {
        this.mensaje = mensaje;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
    
}
PersonaResponse
public class PersonaResponse {

    private String nombres;
    private int edad;

    public String getNombres() {
        return nombres;
    }

    public void setNombres(String nombres) {
        this.nombres = nombres;
    }

    public int getEdad() {
        return edad;
    }

    public void setEdad(int edad) {
        this.edad = edad;
    }
}
ProductoResponse
public class ProductoResponse {

    private String codigo;
    private double precio;
    private String moneda;

    public String getCodigo() {
        return codigo;
    }

    public void setCodigo(String codigo) {
        this.codigo = codigo;
    }

    public double getPrecio() {
        return precio;
    }

    public void setPrecio(double precio) {
        this.precio = precio;
    }

    public String getMoneda() {
        return moneda;
    }

    public void setMoneda(String moneda) {
        this.moneda = moneda;
    }

}
Ahora, la deserialización JSON nos quedaría de la siguiente manera:
ObjectMapper mapper = new ObjectMapper();
//Para persona
GenericResponse<PersonaResponse> response1 = mapper.readValue(jsonString, new TypeReference<GenericResponse<PersonaResponse>>() {});
//Para producto
GenericResponse<ProductoResponse> response2 = mapper.readValue(jsonString, new TypeReference<GenericResponse<ProductoResponse>>() {});

Comentarios