Несколько форм в Java
Одно поле может хранить разные варианты данных:
успех с числом или ошибка с текстом. Ключевое слово
sealed на интерфейсе перечисляет допустимые
формы через permits, а Jackson по аннотациям
выбирает конкретный record при чтении.
Для JSON на базовый тип вешают @JsonTypeInfo
и список @JsonSubTypes. Служебный ключ
type становится дискриминатором, короткое
имя варианта задают в атрибуте name:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Paid.class, name = "paid"),
@JsonSubTypes.Type(value = Declined.class, name = "declined")
})
public sealed interface PayReply permits Paid, Declined {}
public record Paid(int amount) implements PayReply {}
public record Declined(String reason) implements PayReply {}
Запись значения типа PayReply включает
"type":"paid" или "type":"declined"
рядом с полями варианта:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Paid.class, name = "paid"),
@JsonSubTypes.Type(value = Declined.class, name = "declined")
})
public sealed interface PayReply permits Paid, Declined {}
public record Paid(int amount) implements PayReply {}
public record Declined(String reason) implements PayReply {}
public class Main {
public static void main(String[] args) throws Exception {
PayReply ok = new Paid(900);
ObjectMapper mapper = new ObjectMapper();
String text = mapper.writeValueAsString(ok);
System.out.println(text);
PayReply back = mapper.readValue(text, PayReply.class);
System.out.println(back);
}
}
После разбора статический тип снова PayReply,
а фактическая форма определяется при проверке
ветки switch с шаблонами. Без коротких
имён в @JsonSubTypes в текст попали бы
полные имена классов, а не метки протокола.
Опишите sealed интерфейс InboxEvent
с вариантами Opened (поле msgId,
имя "opened") и Archived
(поле folder, имя "archived").
Запишите событие Opened с идентификатором
7 в JSON и выведите строку.
Дана строка JSON:
String raw = "{\"type\":\"archived\",\"folder\":\"2024\"}";
Разберите эту строку
в InboxEvent и выведите имя папки
из восстановленного варианта.