Eu quero serializar e desserializar um objeto imutável usando com.fasterxml.jackson.databind.ObjectMapper.
A classe imutável se parece com isto (apenas 3 atributos internos, getters e construtores):
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;
}
}
No entanto, quando eu executo este teste de unidade:
@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);
}
Eu recebo esta exceção:
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 exceção me pede para criar um construtor padrão, mas este é um objeto imutável, então não quero tê-lo. Como isso definiria os atributos internos? Isso confundiria totalmente o usuário da API.
Portanto, minha pergunta é: Posso de alguma forma desserializar objetos imutáveis sem o construtor padrão?
Respostas:
Para permitir que Jackson saiba como criar um objeto para desserialização, use as anotações
@JsonCreator
e@JsonProperty
para seus construtores, como este:fonte
@JsonCreator
e@JsonProperty
porque não tem acesso ao POJO para alterá-lo? Ainda existe uma maneira de desserializar o objeto?Você pode usar um construtor padrão privado, Jackson irá preencher os campos por meio de reflexão, mesmo se eles forem finais privados.
EDIT: E use um construtor padrão protegido / protegido por pacote para classes pai se você tiver herança.
fonte
A primeira resposta de Sergei Petunin está certa. No entanto, poderíamos simplificar o código removendo anotações @JsonProperty redundantes em cada parâmetro do construtor.
Isso pode ser feito adicionando com.fasterxml.jackson.module.paramnames.ParameterNamesModule em ObjectMapper:
(Btw: este módulo é registrado por padrão no SpringBoot. Se você usar o bean ObjectMapper de JacksonObjectMapperConfiguration ou se você criar seu próprio ObjectMapper com o bean Jackson2ObjectMapperBuilder, então você pode pular o registro manual do módulo)
Por exemplo:
e ObjectMapper desserializa este json sem erros:
fonte