Как предоставить пользовательский десериализатор с помощью Jackson и Spring Boot - PullRequest
0 голосов
/ 26 ноября 2018

У меня есть три следующих приложения:

Проект 1 содержит

  • Бизнес-логика (функция Spring Cloud)
  • Интерфейс IDemoEntity

Проект 2

  • Обработчик, специфичный для AWS
  • Одна реализация IDemoEntity , с аннотациями для DynamoDB
  • Проект основан на Spring Boot

Проект 3

  • Одна реализация IDemoEntity , с аннотацией CosmosDB
  • Обработчик, специфичный для Azure

Классы проекта 1 выглядят так:

public interface IDemoEntity {
    String getName();
    void setName(String name);
}

@Component
public class StoreFunction implements Consumer<Message<IDemoEntity>> {

    @Override
    public void accept(Message<IDemoEntity> t) {

        System.out.println("Stored entity " + t.getPayload().getName());
        return;
    }
}

Для проекта2, реализация IDemoEntity выглядит следующим образом:

@DynamoDBTable(tableName = "DemoEntity")
public class DynamoDemoEntity implements IDemoEntity {

    private String name;
    @Override
    @DynamoDBHashKey
    public String getName() {

        return name;
    }

    @Override
    public void setName(String name) {

        this.name = name;
    }    
}

Для проекта 3 реализация IDemoEntity будет выглядеть аналогично DynamoDemoEntity , но с аннотациями CosmosDB.

Структура может показаться немного сложной, но идея заключается в следующем:

  1. Один раз реализовать бизнес-логику и модель данных (в проекте 1) (используя функцию Spring Cloud)
  2. Реализовать только проект-оболочку для каждой платформы (я начинаю с AWS Lambda в проекте 2, но в проекте 3).для Azure будет выглядеть аналогично), а также вещи, специфичные для платформы (например, реализация сущности, для которой нужны аннотации, специфичные для БД)
  3. Скомпилируйте проект, специфичный для платформы (например, проект 2 для AWS Lambda) с проектом 1 какЗависимость

Я пробовал, и установка работает в основном.Однако есть одна большая проблема:

При вызове StoreFunction выше, Джексон выдает следующее исключение:

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `de.margul.awstutorials.springcloudfunction.logic.IDemoEntity` (no Creators, like default construct, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (String)"{"name": "Detlef"}"; line: 1, column: 1]
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
    at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1452)
    at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1028)
    at com.fasterxml.jackson.databind.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:265)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3004)
    at de.margul.awstutorials.springcloudfunction.aws.handler.RestSpringBootApiGatewayRequestHandler.deserializeBody(RestSpringBootApiGatewayRequestHandler.java:57)
    ... 3 more

Это имеет смысл, потому что Джексон не знает, к реализации которого, если IDemoEntity будет десериализовать полученный JSON.

Самый простой способ сейчас будет заключаться в том, чтобы поместить @JsonDeserialize(as = DynamoDemoEntity.class) в IDemoEntity.

Однако это сломало бы мойПолная структура: у Проекта 1 не должно быть никакой информации, с каким платформенным проектом он компилируется.

Любые идеи, как я мог бы предоставить собственный десериализатор (например, как Spring bean), но без внесения модификаций для платформыв проекте 1?

Ответы [ 2 ]

0 голосов
/ 26 ноября 2018

Если вы хотите десериализовать данный объект для любой сущности, вам нужно написать собственный метод для этого.

Вот пример, который я использовал для того же, и он отлично работает для любой сущности.Пожалуйста, посмотрите и попробуйте.

@Service
public class ObjectSerializer {
    private static final Logger logger = LoggerFactory.getLogger(ObjectSerializer.class);

    private static ObjectMapper objectMapper;

    @Autowired
    private ObjectSerializer(ObjectMapper objectMapper) {
        ObjectSerializer.objectMapper = objectMapper;
    }

    public static <T> T getObject(Object obj, Class<T> class1) {
        String jsonObj = "";
        T objectDto = null;
        try {
            jsonObj = objectMapper.writeValueAsString(obj);
            objectDto =  objectMapper.readValue(jsonObj, class1);
        }catch (IOException e) {
            logger.error("Exception Occured in ObjectSerializer :    |   {}",e.getMessage());
        }
        return objectDto;
    }

Надеюсь, это поможет.

0 голосов
/ 26 ноября 2018

Во-первых, вам нужно создать свой пользовательский DynamoDemoEntityDeserializer, как показано ниже:

class DynamoDemoEntityDeserializer extends JsonDeserializer<DynamoDemoEntity> {
    @Override
    public DynamoDemoEntity deserialize(JsonParser p, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
       // return DynamoDemoEntity instance;
    }
}

Затем вы можете создать бин com.fasterxml.jackson.databind.Module, как показано ниже:

@Bean
public Module dynamoDemoEntityDeserializer() {
    SimpleModule module = new SimpleModule();
    module.addDeserializer(IDemoEntity.class, new DynamoDemoEntityDeserializer());
    return module;
}

Любые компоненты типа com.fasterxml.jackson.databind.Module автоматически регистрируются в автоматически настроенном Jackson2ObjectMapperBuilder и применяются ко всем создаваемым им экземплярам ObjectMapper.Это обеспечивает глобальный механизм добавления пользовательских модулей при добавлении новых функций в ваше приложение.

Источник: howto-customize-the-jackson-objectmapper

...