Chamando o serializador padrão do serializador personalizado em Jackson
1. Introdução
Serializar nossa estrutura de dados completa para JSON usando uma representação exata de todos os campos pode não ser apropriado às vezes ou simplesmente não é o que queremos. Em vez disso,we may want to create an extended or simplified view of our data. É aqui que os serializadores Jackson personalizados entram em ação.
No entanto, a implementação de um serializador personalizado pode ser entediante, especialmente se nossos objetos de modelo tiverem muitos campos, coleções ou objetos aninhados. Felizmente, oJackson library tem várias disposições que podem tornar esse trabalho muito mais simples.
Neste breve tutorial, daremos uma olhada nos serializadores Jackson personalizados e mostraremoshow to access default serializers inside a custom serializer.
2. Modelo de Dados de Amostra
Antes de mergulharmos na personalização de Jackson, vamos dar uma olhada em nossa classeFolder de amostra que queremos serializar:
public class Folder {
private Long id;
private String name;
private String owner;
private Date created;
private Date modified;
private Date lastAccess;
private List files = new ArrayList<>();
// standard getters and setters
}
E a classeFile, que é definida como umList dentro de nossa classeFolder:
public class File {
private Long id;
private String name;
// standard getters and setters
}
3. Serializadores personalizados em Jackson
A principal vantagem do uso de serializadores personalizados é que não precisamos modificar nossa estrutura de classes. Mais,we can easily decouple our expected behavior from the class itself.
Então, vamos imaginar que queremos uma visão reduzida de nossa classeFolder:
Como veremos nas próximas seções, existem várias maneiras de obtermos o resultado desejado em Jackson.
3.1. Abordagem da força bruta
Primeiro, sem usar os serializadores padrão de Jackson, podemos criar um serializador personalizado em que fazemos todo o trabalho pesado sozinhos.
Vamos criar um serializador personalizado para nossa classeFolder para conseguir isso:
public class FolderJsonSerializer extends StdSerializer {
public FolderJsonSerializer() {
super(Folder.class);
}
@Override
public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeStartObject();
gen.writeStringField("name", value.getName());
gen.writeArrayFieldStart("files");
for (File file : value.getFiles()) {
gen.writeStartObject();
gen.writeNumberField("id", file.getId());
gen.writeStringField("name", file.getName());
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeEndObject();
}
}
Assim, podemos serializar nossa classeFolder em uma visão reduzida contendo apenas os campos que desejamos.
3.2. UsandoObjectMapper interno
Embora os serializadores personalizados nos forneçam a flexibilidade de alterar cada propriedade em detalhes, podemos tornar nosso trabalho mais fácil emreusing Jackson’s default serializers.
Uma maneira de usar os serializadores padrão é acessar a classeObjectMapper interna:
Dependendo do caso de uso, podemos precisar estender nossos dados serializados, incluindo mais detalhes paraFolder. Isso pode ser pora legacy system or an external application to be integrated that we do not have a chance to modify.
Vamos mudar nosso serializador para criar um campodetails para nossos dados serializados para simplesmente expor todos os campos da classeFolder:
@Override
public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeStringField("name", value.getName());
provider.defaultSerializeField("files", value.getFiles(), gen);
// this line causes exception
provider.defaultSerializeField("details", value, gen);
gen.writeEndObject();
}
Desta vez, obtemos uma exceçãoStackOverflowError.
When we define a custom serializer, Jackson internally overrides the original BeanSerializer instance que é criado para o tipoFolder. Consequentemente, nossoSerializerProvider encontra o serializador personalizado todas as vezes, em vez do padrão, e isso causa um loop infinito.
Então, como resolvemos esse problema? Veremos uma solução utilizável para este cenário na próxima seção.
5. UsandoBeanSerializerModifier
Uma possível solução é usarBeanSerializerModifierto store the default serializer para o tipoFolderbefore Jackson internally overrides it.
Vamos modificar nosso serializador e adicionar um campo extra -defaultSerializer:
private final JsonSerializer
A seguir, criaremos uma implementação deBeanSerializerModifier para passar o serializador padrão:
public class FolderBeanSerializerModifier extends BeanSerializerModifier {
@Override
public JsonSerializer> modifySerializer(
SerializationConfig config, BeanDescription beanDesc, JsonSerializer> serializer) {
if (beanDesc.getBeanClass().equals(Folder.class)) {
return new FolderJsonSerializer((JsonSerializer) serializer);
}
return serializer;
}
}
Agora, precisamos registrar nossoBeanSerializerModifier como um módulo para fazê-lo funcionar:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.setSerializerModifier(new FolderBeanSerializerModifier());
mapper.registerModule(module);
Em seguida, usamosdefaultSerializer para o campodetails: