middle
Как кастомизировать сериализацию в Jackson?
Jackson предоставляет несколько механизмов кастомизации: пользовательские сериализаторы/десериализаторы, аннотации для управления созданием объектов и mix-ins для работы с классами, которые нельзя модифицировать.
Custom Serializer / Deserializer
Наследуются от StdSerializer / StdDeserializer и применяются через аннотацию @JsonSerialize / @JsonDeserialize.
Пример кастомного сериализатора и десериализатора
// Сериализатор: Money -> "100.50 RUB"
public class MoneySerializer extends StdSerializer<Money> {
public MoneySerializer() { super(Money.class); }
@Override
public void serialize(Money value, JsonGenerator gen,
SerializerProvider provider) throws IOException {
gen.writeString(value.getAmount().toPlainString() + " " + value.getCurrency());
}
}
// Десериализатор: "100.50 RUB" -> Money
public class MoneyDeserializer extends StdDeserializer<Money> {
public MoneyDeserializer() { super(Money.class); }
@Override
public Money deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
String text = p.getText(); // "100.50 RUB"
String[] parts = text.split(" ");
return new Money(new BigDecimal(parts[0]), parts[1]);
}
}
// Применение
public class Order {
@JsonSerialize(using = MoneySerializer.class)
@JsonDeserialize(using = MoneyDeserializer.class)
private Money totalPrice;
}
Аннотации для управления созданием объектов
| Аннотация | Назначение |
|---|---|
@JsonCreator |
Десериализация через конструктор/фабричный метод (для иммутабельных объектов) |
@JsonValue |
Объект сериализуется как одно значение (часто для enum) |
@JsonUnwrapped |
Развёртывание вложенного объекта на уровень выше |
Примеры: JsonCreator, JsonValue, JsonUnwrapped
// @JsonCreator — иммутабельный объект без конструктора по умолчанию
public class Address {
private final String city;
private final String street;
@JsonCreator
public Address(
@JsonProperty("city") String city,
@JsonProperty("street") String street) {
this.city = city;
this.street = street;
}
// Только getters
}
// @JsonValue — enum сериализуется как строка
public enum Status {
ACTIVE("active"), INACTIVE("inactive");
private final String code;
Status(String code) { this.code = code; }
@JsonValue
public String getCode() { return code; }
@JsonCreator
public static Status fromCode(String code) {
return Arrays.stream(values())
.filter(s -> s.code.equals(code))
.findFirst().orElseThrow();
}
}
// @JsonUnwrapped — развёртывание вложенного объекта
public class Person {
private String name;
@JsonUnwrapped
private Address address;
// Вместо {"name":"Иван","address":{"city":"Москва"}}
// Получим: {"name":"Иван","city":"Москва"}
}
Mix-ins для сторонних классов
Позволяют добавить аннотации Jackson к классам, исходный код которых нельзя изменить.
Пример
// Mix-in — абстрактный класс с аннотациями
public abstract class ThirdPartyUserMixin {
@JsonProperty("username")
abstract String getName();
@JsonIgnore
abstract String getSecret();
}
// Регистрация
mapper.addMixIn(ThirdPartyUser.class, ThirdPartyUserMixin.class);
Частые ошибки
- Забытая
@JsonPropertyв параметрах@JsonCreator— Jackson не знает, какой JSON-параметр передать в какой аргумент @JsonUnwrappedне работает с коллекциями иMap— только с POJO- Конфликт
@JsonValueс другими аннотациями —@JsonValueопределяет единственное представление объекта
На собеседовании: назовите
StdSerializer/StdDeserializer,@JsonCreatorдля иммутабельных объектов и mix-ins для сторонних классов. Javarecord-классы (16+) поддерживаются Jackson из коробки, что делает@JsonCreatorменее нужным для простых DTO.