1. Обзор
В этом кратком руководстве мы узнаем, как управлять сериализацией и десериализацией Java Enum с помощью Jackson 2 .
Чтобы копнуть немного глубже и узнать о других интересных вещах, которые мы можем делать с Jackson 2, перейдите к основному учебнику по Джексону .
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. Сериализация перечислений в JSON
3.1. Представление перечисления по умолчанию
По умолчанию Джексон будет представлять перечисления Java как простую строку. Например:
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 { ... }
Это приведет к желаемому результату при сериализации этого перечисления
для расстояния.
МИЛЬ:
{"unit":"miles","meters":1609.34}
3.3. Перечисления и @JsonValue
Еще один простой способ управления выводом маршалинга для перечисления — использование аннотации @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
, который имеет член Distance
:
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
для сериализации перечислений. Мы можем использовать ту же аннотацию и для десериализации. Это возможно, потому что значения Enum являются константами.
Во-первых, давайте используем @JsonValue
с одним из методов получения, getMeters()
:
public enum Distance {
...
@JsonValue
public double getMeters() {
return meters;
}
}
Возвращаемое значение метода getMeters()
представляет объекты Enum. Поэтому при десериализации примера JSON:
{"distance":"0.0254"}
Джексон будет искать объект Enum с возвращаемым значением getMeters()
, равным 0,0254. В данном случае объектом является Расстояние.
ДЮЙМ:
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);
...
}
Используя эту аннотацию, мы просто говорим Джексону сопоставить значение @JsonProperty
с объектом, аннотированным этим значением .
В результате приведенного выше объявления пример строки JSON:
{"distance": "distance-in-km"}
Будет сопоставлен с объектом Distance.KILOMETER :
assertEquals(Distance.KILOMETER, city.getDistance());
4.4. Использование @JsonCreator
Джексон вызывает методы, аннотированные @JsonCreator
, чтобы получить экземпляр окружающего класса.
Рассмотрим представление JSON:
{
"distance": {
"unit":"miles",
"meters":1609.34
}
}
Затем мы определим фабричный метод forValues() с аннотацией
@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;
}
...
}
Обратите внимание на использование аннотации @JsonProperty
для привязки полей JSON к аргументам метода.
Затем, когда мы десериализуем образец JSON, мы получим результат:
assertEquals(Distance.MILE, city.getDistance());
4.5. Использование пользовательского десериализатора
Мы можем использовать собственный десериализатор, если ни один из описанных методов недоступен. Например, у нас может не быть доступа к исходному коду Enum, или мы можем использовать более старую версию Jackson, которая не поддерживает одну или несколько аннотаций, рассмотренных до сих пор.
Согласно нашей пользовательской статье о десериализации , чтобы десериализовать JSON, предоставленный в предыдущем разделе, мы начнем с создания класса десериализации:
public class CustomEnumDeserializer extends StdDeserializer<Distance> {
@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;
}
}
Затем мы будем использовать аннотацию @JsonDeserialize
в Enum, чтобы указать наш пользовательский десериализатор:
@JsonDeserialize(using = CustomEnumDeserializer.class)
public enum Distance {
...
}
И наш результат:
assertEquals(Distance.MILE, city.getDistance());
5. Вывод
В этой статье показано, как получить лучший контроль над процессами и форматами сериализации и десериализации Java Enums .
Реализацию всех этих примеров и фрагментов кода можно найти на GitHub .