Проблема при конвертации json в объект из БД при использовании @JsonTypeInfo

Рейтинг: 0Ответов: 1Опубликовано: 21.03.2023

В БД, в одной из таблиц есть json поле, конвертируемое в определённый класс на бэке.

Этих самых классов достаточно большое количество. Так что для определения в какой класс конвертировать, я использую аннотацию @JsonTypeInfo()

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, property="@class", defaultImpl = OldEventPayload.class)
public interface EventPayload {

 FrontEvent convertToFrontEventDTO();

}

Т.к. в некоторых случаях, там могут быть данные старого типа, в аннотации я использую параметр

defaultImpl = OldEventPayload.class

Указывающий, в какой класс конвертировать, если не удаётся определить нужный.

OldEventPayload:

@JsonDeserialize(using = OldEventPayload.OldPayloadDeserializer.class)
public class OldEventPayload extends AbstractEventPayload {
    private Map<String, Object> body;


    public static class OldPayloadDeserializer extends JsonDeserializer<OldEventPayload> {

        @Override
        public OldEventPayload deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            ObjectMapper mapper = new ObjectMapper();

            JsonNode node = mapper.readTree(p);
            Iterator<String> names = node.fieldNames();

            Map<String, Object> body = new HashMap<>();
            while (names.hasNext()) {
                String name = names.next();
                Object value = node.get(name);
                body.put(name, value);
            }

            OldEventPayload payload = new OldEventPayload();
            payload.setBody(body);
            return payload;
        }
    }
}

Проблема в том, что как только я добавил этот параметр (defaultImpl), Jackson постоянно пытается конвертировать именно в него. (как я понял)

И выдаёт ошибку :

Message: Class app.expert.db.events_new.payloads.OldEventPayload not subtype of [simple type, class app.expert.db.events_new.payloads.InputEmailPayload]; nested exception is java.lang.IllegalArgumentException: Class app.expert.db.events_new.payloads.OldEventPayload not subtype of [simple type, class app.expert.db.events_new.payloads.InputEmailPayload]
Stacktrace: 
    org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:384)
    org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246)
...
...
...
Caused by: Ex - IllegalArgumentException
Message: Class app.expert.db.events_new.payloads.OldEventPayload not subtype of [simple type, class app.expert.db.events_new.payloads.InputEmailPayload]
Stacktrace: 
    com.fasterxml.jackson.databind.type.TypeFactory.constructSpecializedType(TypeFactory.java:359)
    com.fasterxml.jackson.databind.jsontype.impl.StdTypeResolverBuilder.buildTypeDeserializer(StdTypeResolverBuilder.java:128)

В итоге теперь все данные для которых нет подходящего класса, без проблем конвертируются в OldEventPayload, а данные для которых класс определён, наоборот, выдают ошибку.

Что можно сделать, чтобы убрать её?

Ответы

▲ 0

Нашёл решение:

Для остальных классов, типы которых определены, нужно добавить аннотацию

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, property="@class")

Тогда ошибки не возникает и данные нормально конвертируются.

Получается

@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, property="@class", defaultImpl = OldEventPayload.class)
public interface EventPayload {

    FrontEvent convertToFrontEventDTO();

}

@JsonDeserialize(using = OldEventPayload.OldPayloadDeserializer.class)
public class OldEventPayload extends AbstractEventPayload {
    private Map<String, Object> body;

    @Override
    public FrontEvent convertToFrontEventDTO() {
        ....
    }
    @Override
    public OldEventPayload deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        ....
    }
}


@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, property="@class")
public class NNNPayload extends AbstractEventPayload {
    private String assignee;

    @Override
    public FrontEvent convertToFrontEventDTO() {
        ........
    }
}