Como serializar e desserializar enums com Jackson
1. Visão geral
Este tutorial rápido mostrará como controlar a formaJava Enums are serialized and deserialized with Jackson 2.
Para cavar um pouco mais fundo e aprenderother cool things we can do Jackson 2 - vá parathe main Jackson tutorial.
2. Controlando a representação Enum
Vamos definir o seguinte Enum:
public enum Distance {
KILOMETER("km", 1000),
MILE("miles", 1609.34),
METER("meters", 1),
INCH("inches", 0.0254),
CENTIMETER("cm", 0.01),
MILLIMETER("mm", 0.001);
private String unit;
private final double meters;
private Distance(String unit, double meters) {
this.unit = unit;
this.meters = meters;
}
// standard getters and setters
}
3. Serializando Enums para JSON
3.1. Representação Enum Padrão
Por padrão, Jackson representará Java Enums como uma String simples - por exemplo:
new ObjectMapper().writeValueAsString(Distance.MILE);
Vai resultar em:
"MILE"
O que gostaríamos de obter ao empacotar esteEnum to a JSON Object é fornecer algo como:
{"unit":"miles","meters":1609.34}
3.2. Enum como objeto JSON
A partir do Jackson 2.1.2, agora existe uma opção de configuração que pode lidar com esse tipo de representação. Isso pode ser feito por meio da anotação@JsonFormat no nível da classe:
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Distance { ... }
Isso levará ao resultado desejado ao serializar esteenum paraDistance.MILE:
{"unit":"miles","meters":1609.34}
3.3. Enums e@JsonValue
Ainda outra maneira simples de controlar a saída de empacotamento para um enum é usar a anotação@JsonValue em um getter:
public enum Distance {
...
@JsonValue
public String getMeters() {
return meters;
}
}
O que estamos expressando aqui é quegetMeters() é a representação real deste enum. Portanto, o resultado da serialização será:
1609.34
3.4. Serializador personalizado para Enum
Antes do Jackson 2.1.2, ou se ainda mais customização for necessária para o enum, podemos usar umcustom Jackson serializer. Primeiro, precisamos defini-lo:
public class DistanceSerializer extends StdSerializer {
public DistanceSerializer() {
super(Distance.class);
}
public DistanceSerializer(Class t) {
super(t);
}
public void serialize(Distance distance, JsonGenerator generator, SerializerProvider provider)
throws IOException, JsonProcessingException {
generator.writeStartObject();
generator.writeFieldName("name");
generator.writeString(distance.name());
generator.writeFieldName("unit");
generator.writeString(distance.getUnit());
generator.writeFieldName("meters");
generator.writeNumber(distance.getMeters());
generator.writeEndObject();
}
}
Agora, aplicaremos o serializador à classe que será serializada:
@JsonSerialize(using = DistanceSerializer.class)
public enum TypeEnum { ... }
O que resulta em:
{"name":"MILE","unit":"miles","meters":1609.34}
4. Desserializando JSON para Enum
Primeiro, vamos definir uma classeCity que tem um membroDistance:
public class City {
private Distance distance;
...
}
A seguir, discutiremos as diferentes maneiras de desserializar uma string JSON para um Enum.
4.1. Comportamento padrão
By default, Jackson will use the Enum name to deserialize from JSON.
Por exemplo, ele desserializará o JSON:
{"distance":"KILOMETER"}
Para um objetoDistance.KILOMETER:
City city = new ObjectMapper().readValue(json, City.class);
assertEquals(Distance.KILOMETER, city.getDistance());
4.2. Usando@JsonValue
Aprendemos como usar@JsonValue para serializar Enums. Também podemos usar a mesma anotação para desserialização. Isso é possível porque os valores de enumeração são constantes.
Primeiro, vamos usar@JsonValue com um dos métodos getter -getMeters():
public enum Distance {
...
@JsonValue
public double getMeters() {
return meters;
}
}
Agora, o valor de retorno do métodogetMeters() representa os objetos Enum. Assim, ao desserializar a amostra JSON:
{"distance":"0.0254"}
Jackson irá procurar o objeto Enum que tem um valor de retornogetMeters() de 0,0254. Nesse caso, o objeto éDistance.INCH:
assertEquals(Distance.INCH, city.getDistance());
4.3. Usando@JsonProperty
A anotação@JsonProperty é usada em instâncias de enumeração:
public enum Distance {
@JsonProperty("distance-in-km")
KILOMETER("km", 1000),
@JsonProperty("distance-in-miles")
MILE("miles", 1609.34);
...
}
Usando esta anotação,we are simply telling Jackson to map the value of the @JsonProperty to the object annotated with this value.
Como resultado da declaração acima, o exemplo JSON string:
{"distance": "distance-in-km"}
Será mapeado para o objetoDistance.KILOMETER:
assertEquals(Distance.KILOMETER, city.getDistance());
4.4. Usando@JsonCreator
Jackson invoca métodos anotados com@JsonCreator para obter uma instância da classe envolvente.
Considere a representação JSON:
{
"distance": {
"unit":"miles",
"meters":1609.34
}
}
Agora, vamos definir o método de fábricaforValues() com a anotação@JsonCreator:
public enum Distance {
@JsonCreator
public static Distance forValues(@JsonProperty("unit") String unit,
@JsonProperty("meters") double meters) {
for (Distance distance : Distance.values()) {
if (distance.unit.equals(unit) && Double.compare(distance.meters, meters) == 0) {
return distance;
}
}
return null;
}
...
}
Observe o uso da anotação@JsonProperty para vincular os campos JSON aos argumentos do método.
Então, quando desserializarmos a amostra JSON, obteremos o resultado:
assertEquals(Distance.MILE, city.getDistance());
4.5. Usando umDeserializer personalizado
Um desserializador personalizado pode ser usado se nenhuma das técnicas descritas estiver disponível. Por exemplo, podemos não ter acesso ao código-fonte Enum, ou podemos estar usando uma versão mais antiga de Jackson que não oferece suporte a uma ou mais das anotações abordadas até agora.
De acordo comour custom deserialization article, para desserializar o JSON fornecido na seção anterior, vamos começar criando a classe de desserialização:
public class CustomEnumDeserializer extends StdDeserializer {
@Override
public Distance deserialize(JsonParser jsonParser, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
String unit = node.get("unit").asText();
double meters = node.get("meters").asDouble();
for (Distance distance : Distance.values()) {
if (distance.getUnit().equals(unit) && Double.compare(distance.getMeters(), meters) == 0) {
return distance;
}
}
return null;
}
}
Em seguida, usamos a anotação@JsonDeserialize no Enum para especificar nosso desserializador personalizado:
@JsonDeserialize(using = CustomEnumDeserializer.class)
public enum Distance {
...
}
E nosso resultado é:
assertEquals(Distance.MILE, city.getDistance());
5. Conclusão
Este artigo ilustrou como obter melhor controle sobreserialization and deserialization processes and formats of Java Enums.
A implementação de todos esses exemplos e trechos de código pode ser encontradaover on Github.