Introducción
En este tutorial, aprenderemos cómo ignorar campos dinámicamente en Jackson. Este conocimiento es crucial para programadores que desean serializar y deserializar objetos en Java de manera flexible, dependiendo de condiciones específicas. Examinaremos tres enfoques para lograr este objetivo: @JsonFilter, @JsonView y Jackson Mixins.
Configuración del Proyecto
Para comenzar con este tutorial, debemos añadir la biblioteca necesaria de Jackson a nuestro proyecto. Aquí tienes el fragmento de código que debes incluir en tu archivo pom.xml
si utilizas Maven:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
La última versión puede ser consultada aquí.
Ignorar Dinámicamente Usando @JsonFilter
El primer enfoque que examinaremos es mediante una anotación que especifica un filtro que se utilizará durante la serialización. Al anotar una clase con @JsonFilter, podemos configurar dinámicamente qué campos se incluirán durante la serialización.
Ejemplo de Código
import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
@JsonFilter("publicFilter")
public class UserWithFilter {
private Long id;
private String name;
// Constructor, getters y setters
public UserWithFilter(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
}
// En tu método principal
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("publicFilter", SimpleBeanPropertyFilter.serializeAllExcept("id"));
ObjectMapper objectMapper = new ObjectMapper().setFilterProvider(filterProvider);
UserWithFilter user = new UserWithFilter(1000L, "John");
String result = objectMapper.writeValueAsString(user);
System.out.println(result); // Debería contener "John" y no el "1000"
Este método proporciona una gran flexibilidad, ya que nos permite escoger qué campos serializar en tiempo de ejecución. Sin embargo, no funciona para la deserialización; incluso si personalizamos el ObjectMapper
con el mismo filtro, el campo id será unmarshalled en el objeto.
Deserialización:
String json = "{\"id\":1000,\"name\":\"John\"}";
UserWithFilter result = objectMapper.readValue(json, UserWithFilter.class);
System.out.println(result.getId()); // Imprime "1000"
System.out.println(result.getName()); // Imprime "John"
Ignorar Condicionalmente Usando @JsonView
A diferencia del enfoque anterior, @JsonView permite controlar la visibilidad de los campos al definir diferentes vistas. Esta opción debe definirse en tiempo de compilación.
Ejemplo de Código
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
public class UserWithView {
@JsonView(InternalView.class)
private Long id;
@JsonView(PublicView.class)
private String name;
// Constructor, getters y setters
public UserWithView(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public interface PublicView {}
public interface InternalView extends PublicView {}
}
// En tu método principal
ObjectWriter objectWriter = new ObjectMapper().writerWithView(UserWithView.PublicView.class);
String result = objectWriter.writeValueAsString(user);
System.out.println(result); // Debería contener "John" y no el "1000"
Este enfoque también funciona bien durante la deserialización:
Deserialización:
String json = "{\"id\":1000,\"name\":\"John\"}";
ObjectReader objectReader = new ObjectMapper().readerWithView(UserWithView.PublicView.class)
.forType(UserWithView.class);
UserWithView user = objectReader.readValue(json);
System.out.println(user.getName()); // Imprime "John"
Usar Mixins para Aplicar @JsonIgnore Dinámicamente
Los Mixins de Jackson nos permiten aplicar anotaciones como @JsonIgnore sin modificar la clase original. Necesitamos definir una interfaz con un método getter en la propiedad que queremos ignorar.
Ejemplo de Código
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
public interface PublicMixin {
@JsonIgnore
Long getId();
}
// En tu método principal
ObjectMapper objectMapper = new ObjectMapper().addMixIn(UserWithMixin.class, PublicMixin.class);
String result = objectMapper.writeValueAsString(user);
System.out.println(result); // Debería contener "John" y no el "1000"
// Deserialización
String json = "{\"id\":1000,\"name\":\"John\"}";
UserWithMixin user = objectMapper.readValue(json, UserWithMixin.class);
System.out.println(user.getId()); // Debería ser null
System.out.println(user.getName()); // Imprime "John"
Comparando los Enfoques
Enfoque | Flexibilidad Runtime | Caso de Uso | Serialización / Deserialización |
---|---|---|---|
@JsonFilter | Alta | Excluir campos dinámicamente en tiempo de ejecución | No |
@JsonView | Media | Definir múltiples vistas de serialización | Sí |
Mixins | Media | Aplicar @JsonIgnore sin modificar la clase original | Sí |
La elección del enfoque depende de la necesidad de flexibilidad en tiempo de ejecución y si es crucial que la deserialización funcione correctamente.
Conclusión
En este artículo, hemos explorado varios enfoques para lograr la dinámica en la serialización y deserialización de objetos con Jackson. Aunque la plena dinámica en lenguajes compilados como Java puede ser difícil de alcanzar, Jackson proporciona varios mecanismos para personalizar el comportamiento de la serialización y adaptar las salidas según diferentes necesidades en tiempo de ejecución.
Implementar estos enfoques te ayudará a manejar con eficacia cómo se representan los datos en tu aplicación, mejorando la versatilidad de tu código Java.