Come serializzare e deserializzare gli enum con Jackson

1. Panoramica

Questo breve tutorial mostrerà come controllare il modo in cui Java Enums viene serializzato e deserializzato con Jackson 2 .

Per scavare un po 'più a fondo e imparare altre cose interessanti possiamo fare Jackson 2 - vai al tutorial principale di Jackson.

2. Controllo della rappresentazione Enum

Definiamo il seguente 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. Serializzazione di enumerazioni in JSON

3.1. Rappresentazione Enum predefinita

Per impostazione predefinita, Jackson rappresenterà Java Enums come semplice stringa, ad esempio:

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

Risulterà in:

"MILE"

Quello che vorremmo ottenere quando effettuiamo il marshalling di questo Enum su un oggetto JSON è dare qualcosa come:

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

3.2. Enum come oggetto JSON

A partire da Jackson 2.1.2, ora è disponibile un'opzione di configurazione in grado di gestire questo tipo di rappresentazione. Questo può essere fatto tramite l' annotazione @JsonFormat a livello di classe:

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

Ciò porterà al risultato desiderato durante la serializzazione di questa enumerazione per Distanza. MIGLIO:

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

3.3. Enums e @JsonValue

Ancora un altro modo semplice per controllare l'output di marshalling per un'enumerazione è usare l' annotazione @JsonValue su un getter:

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

Quello che stiamo esprimendo qui è che getMeters () è la rappresentazione effettiva di questo enum. Quindi, il risultato della serializzazione sarà:

1609.34

3.4. Serializzatore personalizzato per Enum

Prima di Jackson 2.1.2, o se è richiesta ancora più personalizzazione per l'enum, possiamo usare un serializzatore Jackson personalizzato. Innanzitutto, dovremo definirlo:

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

Applicheremo ora il serializzatore alla classe che verrà serializzata:

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

Che si traduce in:

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

4. Deserializzazione di JSON in Enum

Per prima cosa, definiamo una classe City che ha un membro Distance :

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

Successivamente, discuteremo i diversi modi di deserializzare una stringa JSON in un Enum.

4.1. Comportamento predefinito

Per impostazione predefinita, Jackson utilizzerà il nome Enum per deserializzare da JSON .

Ad esempio, deserializzerà il JSON:

{"distance":"KILOMETER"}

A un oggetto Distance.KILOMETER :

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

4.2. Utilizzando @JsonValue

Abbiamo imparato come usare @JsonValue per serializzare Enums . Possiamo usare la stessa annotazione anche per la deserializzazione. Ciò è possibile perché i valori Enum sono costanti.

Per prima cosa, usiamo @JsonValue con uno dei metodi getter - getMeters () :

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

Ora, il valore di ritorno del metodo getMeters () rappresenta gli oggetti Enum. Pertanto, durante la deserializzazione del JSON di esempio:

{"distance":"0.0254"}

Jackson cercherà l'oggetto Enum che ha un valore di ritorno getMeters () di 0,0254. In questo caso, l'oggetto è Distanza. POLLICI:

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

4.3. Utilizzando @JsonProperty

L' annotazione @JsonProperty viene utilizzata nelle istanze di enumerazione:

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

Usando questa annotazione, stiamo semplicemente dicendo a Jackson di mappare il valore di @JsonProperty all'oggetto annotato con questo valore .

Come risultato della dichiarazione precedente, la stringa JSON di esempio:

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

Verrà mappato sull'oggetto Distance.KILOMETER :

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

4.4. Utilizzando @JsonCreator

Jackson invoca metodi annotati con @JsonCreator per ottenere un'istanza della classe che lo racchiude.

Considera la rappresentazione JSON:

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

Ora definiamo il metodo factory forValues ​​() con l' annotazione @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; } ... }

Notare l'uso dell'annotazione @JsonProperty per associare i campi JSON con gli argomenti del metodo.

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

Questo articolo ha illustrato come ottenere un migliore controllo sui processi di serializzazione e deserializzazione e sui formati di Java Enums .

L'implementazione di tutti questi esempi e frammenti di codice può essere trovata su GitHub.