Как да сериализираме и десериализираме Enums с Джаксън

1. Общ преглед

Този бърз урок ще покаже как да контролирате начина, по който Java Enums се сериализират и десериализират с Jackson 2 .

За да разровим малко по-дълбоко и да научим други страхотни неща, можем да направим Jackson 2 - насочете се към основния урок за Jackson.

2. Контрол на представителството на Enum

Нека дефинираме следния 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. Сериализиране на Enums до JSON

3.1. Представяне на Enum по подразбиране

По подразбиране Jackson ще представя Java Enums като прост String - например:

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

Ще доведе до:

"MILE"

Това, което бихме искали да получим, когато маршираме този Enum към JSON обект, е да дадем нещо като:

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

3.2. Enum като JSON обект

Започвайки с Jackson 2.1.2, вече има опция за конфигуриране, която може да се справи с този вид представяне. Това може да стане чрез анотацията @JsonFormat на ниво клас:

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

Това ще доведе до желания резултат при сериализиране на това изброяване за разстояние. MILE:

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

3.3. Enums и @JsonValue

Още един прост начин за управление на изхода за марширане на enum е използването на анотацията @JsonValue на гетер :

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

Това, което изразяваме тук, е, че getMeters () е действителното представяне на това изброяване. Така че резултатът от сериализацията ще бъде:

1609.34

3.4. Персонализиран сериализатор за Enum

Преди Jackson 2.1.2, или ако се изисква още повече персонализация за изброяването, можем да използваме персонализиран сериализатор на Jackson. Първо ще трябва да го дефинираме:

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

Сега ще приложим сериализатора към класа, който ще бъде сериализиран:

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

Което води до:

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

4. Десериализиране на JSON към Enum

Първо, нека дефинираме клас City, който има член на разстояние :

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

След това ще обсъдим различните начини за десериализиране на JSON низ в Enum.

4.1. Поведение по подразбиране

По подразбиране Джаксън ще използва името Enum, за да десериализира от JSON .

Например ще десериализира JSON:

{"distance":"KILOMETER"}

Към обект Distance.KILOMETER :

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

4.2. Използване на @JsonValue

Научихме как да използваме @JsonValue за сериализиране на Enums. Можем да използваме същата анотация и за десериализация. Това е възможно, защото стойностите на Enum са константи.

Първо, нека използваме @JsonValue с един от методите за получаване - getMeters () :

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

Сега връщаната стойност на метода getMeters () представлява обектите Enum. По този начин, когато десериализира пробата JSON:

{"distance":"0.0254"}

Джаксън ще търси обекта Enum, който има getMeters () върната стойност 0,0254. В този случай обектът е Разстояние. INCH:

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

4.3. Използване на @JsonProperty

В @JsonProperty анотацията се използва за изброяване случаи:

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

Тази статия илюстрира как да се получи по-добър контрол върху процесите на сериализация и десериализация и форматите на Java Enums .

Прилагането на всички тези примери и кодови фрагменти може да бъде намерено в GitHub.