Автор оригинала: Eugen Paraschiv.
1. Обзор
Этот краткий учебник покажет, как управлять тем, как Перечисления Java сериализуются и десериализуются с помощью Jackson 2 .
Чтобы копнуть немного глубже и узнать другие интересные вещи, которые мы можем сделать в Jackson 2 – перейдите к основному учебнику Джексона .
2. Управление представлением перечисления
Давайте определим следующее перечисление:
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"
То, что мы хотели бы получить при маршалировании этого перечисления в объект JSON , – это дать что-то вроде:
{"unit":"miles","meters":1609.34}
3.2. Перечисление в качестве объекта JSON
Начиная с версии 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; } }
То, что мы выражаем здесь, заключается в том, что get Meters() является фактическим представлением этого перечисления. Таким образом, результатом сериализации будет:
1609.34
3.4. Пользовательский сериализатор для перечисления
Перед Jackson 2.1.2, или если для перечисления требуется еще больше настроек, мы можем использовать пользовательский сериализатор Джексона. Во-первых, нам нужно будет определить его:
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 в перечисление
Во-первых, давайте определим класс City , который имеет член Distance :
public class City { private Distance distance; ... }
Далее мы обсудим различные способы десериализации строки JSON в перечисление.
4.1. Поведение по умолчанию
По умолчанию Джексон будет использовать имя перечисления для десериализации из JSON .
Например, он десериализует JSON:
{"distance":"KILOMETER"}
На Расстояние.КИЛОМЕТР объект:
City city = new ObjectMapper().readValue(json, City.class); assertEquals(Distance.KILOMETER, city.getDistance());
4.2. Использование @JsonValue
Мы научились использовать @JsonValue для сериализации перечислений. Мы можем использовать ту же аннотацию и для десериализации. Это возможно, потому что значения перечисления являются константами.
Во — первых, давайте использовать @JsonValue с одним из методов getter – getMeters() :
public enum Distance { ... @JsonValue public double getMeters() { return meters; } }
Теперь возвращаемое значение метода getMeters() представляет объекты перечисления. Таким образом, при десериализации образца JSON:
{"distance":"0.0254"}
Джексон будет искать объект перечисления, который имеет 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"}
Будет сопоставлено с расстоянием .КИЛОМЕТР объект:
assertEquals(Distance.KILOMETER, city.getDistance());
4.4. Использование @JsonCreator
Джексон вызывает методы с аннотациями @JsonCreator чтобы получить экземпляр заключающего класса.
Рассмотрим представление JSON:
{ "distance": { "unit":"miles", "meters":1609.34 } }
Теперь давайте определим метод forValues() factory с помощью аннотации @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, или мы можем использовать более старую версию Джексона, которая не поддерживает одну или несколько аннотаций, описанных до сих пор.
В соответствии с нашей статьей о пользовательской десериализации , чтобы десериализовать JSON, предоставленный в предыдущем разделе, мы начнем с создания класса десериализации:
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; } }
Затем мы используем аннотацию @JsonDeserialize в перечислении, чтобы указать наш пользовательский десериализатор:
@JsonDeserialize(using = CustomEnumDeserializer.class) public enum Distance { ... }
И наш результат таков:
assertEquals(Distance.MILE, city.getDistance());
5. Заключение
В этой статье показано, как получить лучший контроль над процессами сериализации и десериализации и форматами перечислений Java .
Реализацию всех этих примеров и фрагментов кода можно найти на GitHub .