Deserialización con Jackson usando el patrón de diseño Builder
El patrón de diseño Builder es uno de esos patrones que son ampliamente utilizados por los desarrolladores. Es muy útil para crear clases que tienen muchas propiedades y/o cuando algunas de ellas son opcionales. Cuando trabajamos con builders suele ser bastante común que los constructores de nuestras clases sean privados y que solo se puedan crear instancias de las mismas a través de su builder. Por este motivo, si usamos estas clases para deserializar con Jackson puede que nos encontremos con problemas, ya que Jackson no puede crear instancias de nuestras clases debido a que no tienen un constructor público.
En este tutorial vamos a mostrar como usar nuestro builders para deserializar JSON con Jackson. También mostraremos como hacer esto si utilizamos Lombok para generar nuestros builders.
Deserialización con Jackson usando un builder
Lo primero que vamos a hacer es crear una clase de ejemplo que utilizaremos durante el resto del tutorial.
Vamos a crear una clase Address que simplemente modela una dirección:
Como podemos ver, esta clase es inmutable y sólo se pueden crear instancias de ella a través de su builder.
Si intentamos deserializar un JSON en nuestra clase Address, Jackson lanzará una excepción diciendo que no puede encontrar un constructor adecuado para crear instancias de nuestra clase:
Esto ocurre porque el constructor de nuestra clase es privado.
Lo que tenemos que hacer es decirle a Jackson que utilice nuestro builder para la deserialización de la clase. Podemos hacer esto anotando nuestra clase con @JsonDeserialize y especificandole nuestra clase Builder:
Sin embargo, todavía obtendremos otra excepción si tratamos de usar esta clase tal y como la tenemos ahora mismo:
Ahora Jackson nos dice que no puede reconocer las propiedades de nuestra clase Builder. Esto ocurre porque Jackson espera que nuestro builder utilice el perfijo with en los métodos setter. Y en nuestro caso no utilizamos ningún prefijo.
Una opción para solucionar esto es cambiar nuestro builder para usar el prefijo with. Otra opción es indicarle a Jackson que use un prefijo diferente a través de la anotación @JsonPOJOBuilder en nuestra clase Builder:
En este caso, hemos dejado el prefijo como un string vacío ya que no usamos ningún prefijo.
Además, es importante tener en cuenta que Jackson espera que el métodod que crea instancias en nuestro builder se llame build. Si queremos utilizar otro nombre lo tenemos que especificar en esta misma anotación:
En este ejemplo hemos supuesto que el método de nuestro builder se llama create.
Deserializando usando Lombok para generar nuestros builders
Vamos a hacer ahora lo mismo que en la sección anterior pero trabajando con una clase que usa Lombok para generar su builder.
Primero, vamos a modificar nuestra clase Addresss para añadir las anotaciones de Lombok necesarias:
Esta clase también es inmutable y tiene constructor privado. Por tanto, sólo se pueden crear instancias de la misma a través de su builder.
Observemos también que nuestro builder ahora usa el prefijo with en los métodos setter. Por tanto, simplemente necesitamos decirle a Jackson que utilice nuestra clase Builder y ya estaríamos listos:
Esto es suficiente para utilizar esta clase con Jackson a la hora de deserializar.
Sin embargo, imaginémonos que no queremos usar el prefijo with en nuestro builder, ¿cómo podemos decirle a Jackson que utilize otro prefijo? Para ello, tenemos que declarar la clase Builder explícitamente y usar la anotación @JsonPOJOBuilder como hicimos anteriormente:
Es importante mencionar que Lombok usa build como el nombre por defecto para el métodod que crea las instancias en los builders. Si queremos utilizar un nombre distinto tenemos que especifiarlo en la propiedad buildMethodName de la anotación @JsonPOJOBuilder como hicimos en la sección anterior.
@Jacksonized
En la versión 1.18.14 Lombok ha introducido una funcionalidad experimental llamada @Jacksonized que pretende simplificar la integración entre Lombok y Jackson.
Modifiquemos ahora nuestro ejemplo anterior para usar estar anotación. Es tan sencillo como reemplazar todas nuestras anotaciones de Jackson con @Jacksonized:
¡Ya hemos acabado! ¡Nuestra clase está lista!
Además, si cambiamos el prefijo de los métodos setter o el nombre del método buid de nuestro builder, Lombok se encargará de modificar las anotaciones generadas de Jackson en consecuencia.
Esta anotación es muy útil y nos permite ahorrarnos código y tiempo a la hora de trabajar con builders y Jackson. Pero recordemos que, en el momento de escribir este tutorial, esta anotación todavía es experimental.
Conclusión
En este tutorial hemos explicado como utilizar Jackson para deserializar clases que utilizan el patrón de diseño Builder. Hemos mostrado ejemplos de como hacerlo y también hemos enseñado como personalizar el comportamiento por defecto en las anotaciones de Jackson. Por último, también hemos incluido como hacer esto mismo pero utilizando la librería Lombok para generar nuestros builders.
El código fuente de los ejemplos se encuentra disponible en Github.
Deja un comentario