Comment sérialiser et désérialiser les énumérations avec Jackson

1. Vue d'ensemble

Ce tutoriel rapide montrera comment contrôler la façon dont les Java Enums sont sérialisés et désérialisés avec Jackson 2 .

Pour creuser un peu plus et apprendre d' autres choses intéressantes, nous pouvons faire Jackson 2 - dirigez-vous vers le didacticiel principal de Jackson.

2. Contrôle de la représentation Enum

Définissons l'énumération suivante:

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. Sérialisation des énumérations en JSON

3.1. Représentation enum par défaut

Par défaut, Jackson représentera les énumérations Java sous forme de chaîne simple - par exemple:

new ObjectMapper().writeValueAsString(Distance.MILE);

Aura pour résultat:

"MILE"

Ce que nous aimerions obtenir lors du marshaling de cet Enum vers un objet JSON est de donner quelque chose comme:

{"unit":"miles","meters":1609.34} 

3.2. Enum en tant qu'objet JSON

À partir de Jackson 2.1.2, il existe maintenant une option de configuration qui peut gérer ce type de représentation. Cela peut être fait via l' annotation @JsonFormat au niveau de la classe:

@JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum Distance { ... }

Cela conduira au résultat souhaité lors de la sérialisation de cette énumération pour Distance. MILE:

{"unit":"miles","meters":1609.34}

3.3. Enums et @JsonValue

Un autre moyen simple de contrôler la sortie de marshaling pour une énumération consiste à utiliser l' annotation @JsonValue sur un getter:

public enum Distance { ... @JsonValue public String getMeters() { return meters; } }

Ce que nous exprimons ici, c'est que getMeters () est la représentation réelle de cette énumération. Ainsi, le résultat de la sérialisation sera:

1609.34

3.4. Sérialiseur personnalisé pour Enum

Avant Jackson 2.1.2, ou si encore plus de personnalisation est requise pour l'énumération, nous pouvons utiliser un sérialiseur Jackson personnalisé. Tout d'abord, nous devrons le définir:

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(); } }

Nous allons maintenant appliquer le sérialiseur à la classe qui sera sérialisée:

@JsonSerialize(using = DistanceSerializer.class) public enum TypeEnum { ... }

Ce qui se traduit par:

{"name":"MILE","unit":"miles","meters":1609.34}

4. Désérialisation de JSON en Enum

Tout d'abord, définissons une classe City qui a un membre Distance :

public class City { private Distance distance; ... }

Ensuite, nous allons discuter des différentes façons de désérialiser une chaîne JSON en un Enum.

4.1. Comportement par défaut

Par défaut, Jackson utilisera le nom Enum pour désérialiser de JSON .

Par exemple, cela désérialisera le JSON:

{"distance":"KILOMETER"}

Vers un objet Distance.KILOMETER :

City city = new ObjectMapper().readValue(json, City.class); assertEquals(Distance.KILOMETER, city.getDistance());

4.2. Utilisation de @JsonValue

Nous avons appris à utiliser @JsonValue pour sérialiser les Enums. Nous pouvons également utiliser la même annotation pour la désérialisation. Cela est possible car les valeurs Enum sont des constantes.

Tout d'abord, utilisons @JsonValue avec l'une des méthodes getter - getMeters () :

public enum Distance { ... @JsonValue public double getMeters() { return meters; } }

Désormais, la valeur de retour de la méthode getMeters () représente les objets Enum. Ainsi, lors de la désérialisation de l'exemple JSON:

{"distance":"0.0254"}

Jackson recherchera l'objet Enum qui a une valeur de retour getMeters () de 0,0254. Dans ce cas, l'objet est Distance. POUCE:

assertEquals(Distance.INCH, city.getDistance()); 

4.3. Utilisation de @JsonProperty

L' annotation @JsonProperty est utilisée sur les instances d'énumération:

public enum Distance { @JsonProperty("distance-in-km") KILOMETER("km", 1000), @JsonProperty("distance-in-miles") MILE("miles", 1609.34); ... }

By using this annotation, we are simply telling Jackson to map the value of the @JsonProperty to the object annotated with this value.

As a result of the above declaration, the example JSON string:

{"distance": "distance-in-km"}

Will be mapped to the Distance.KILOMETER object:

assertEquals(Distance.KILOMETER, city.getDistance());

4.4. Using @JsonCreator

Jackson invokes methods annotated with @JsonCreator to get an instance of the enclosing class.

Consider the JSON representation:

{ "distance": { "unit":"miles", "meters":1609.34 } }

Now, let's define the forValues() factory method with the @JsonCreator annotation:

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; } ... }

Note the use of @JsonProperty annotation to bind the JSON fields with the method arguments.

Then, when we deserialize the JSON sample, we'll get the result:

assertEquals(Distance.MILE, city.getDistance());

4.5. Using a Custom Deserializer

A custom deserializer can be used if none of the described techniques are available. For example, we might have no access to the Enum source code, or we might be using an older Jackson version that doesn't support one or more of the annotations covered so far.

According to our custom deserialization article, in order to deserialize the JSON provided in the previous section, we'll start by creating the deserialization class:

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; } } 

Next, we use the @JsonDeserialize annotation on the Enum to specify our custom deserializer:

@JsonDeserialize(using = CustomEnumDeserializer.class) public enum Distance { ... }

And our result is:

assertEquals(Distance.MILE, city.getDistance());

5. Conclusion

Cet article a illustré comment mieux contrôler les processus et les formats de sérialisation et de désérialisation des énumérations Java .

L'implémentation de tous ces exemples et extraits de code peut être trouvée sur GitHub.