Quiero serializar y deserializar un objeto inmutable usando com.fasterxml.jackson.databind.ObjectMapper.
La clase inmutable se ve así (solo 3 atributos internos, captadores y constructores):
public final class ImportResultItemImpl implements ImportResultItem {
private final ImportResultItemType resultType;
private final String message;
private final String name;
public ImportResultItemImpl(String name, ImportResultItemType resultType, String message) {
super();
this.resultType = resultType;
this.message = message;
this.name = name;
}
public ImportResultItemImpl(String name, ImportResultItemType resultType) {
super();
this.resultType = resultType;
this.name = name;
this.message = null;
}
@Override
public ImportResultItemType getResultType() {
return this.resultType;
}
@Override
public String getMessage() {
return this.message;
}
@Override
public String getName() {
return this.name;
}
}
Sin embargo, cuando ejecuto esta prueba unitaria:
@Test
public void testObjectMapper() throws Exception {
ImportResultItemImpl originalItem = new ImportResultItemImpl("Name1", ImportResultItemType.SUCCESS);
String serialized = new ObjectMapper().writeValueAsString((ImportResultItemImpl) originalItem);
System.out.println("serialized: " + serialized);
//this line will throw exception
ImportResultItemImpl deserialized = new ObjectMapper().readValue(serialized, ImportResultItemImpl.class);
}
Obtengo esta excepción:
com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class eu.ibacz.pdkd.core.service.importcommon.ImportResultItemImpl]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: {"resultType":"SUCCESS","message":null,"name":"Name1"}; line: 1, column: 2]
at
... nothing interesting here
Esta excepción me pide que cree un constructor predeterminado, pero este es un objeto inmutable, por lo que no quiero tenerlo. ¿Cómo establecería los atributos internos? Confundiría totalmente al usuario de la API.
Entonces, mi pregunta es: ¿Puedo de alguna manera deserializar objetos inmutables sin el constructor predeterminado?
Respuestas:
Para que Jackson sepa cómo crear un objeto para la deserialización, use las anotaciones
@JsonCreator
y@JsonProperty
para sus constructores, así:fuente
@JsonCreator
y@JsonProperty
porque no tiene acceso al POJO para cambiarlo? ¿Todavía hay una forma de deserializar el objeto?Puede usar un constructor predeterminado privado, Jackson luego completará los campos mediante la reflexión, incluso si son finales privados.
EDITAR: Y use un constructor predeterminado protegido / protegido por paquete para las clases principales si tiene herencia.
fuente
La primera respuesta de Sergei Petunin es correcta. Sin embargo, podríamos simplificar el código eliminando las anotaciones @JsonProperty redundantes en cada parámetro del constructor.
Se puede hacer agregando com.fasterxml.jackson.module.paramnames.ParameterNamesModule en ObjectMapper:
(Por cierto: este módulo está registrado de forma predeterminada en SpringBoot. Si usa el bean ObjectMapper de JacksonObjectMapperConfiguration o si crea su propio ObjectMapper con el bean Jackson2ObjectMapperBuilder, puede omitir el registro manual del módulo)
Por ejemplo:
y ObjectMapper deserializa este json sin ningún error:
fuente