Интеграция Spring с Jackson ObjectMapper и Java 8 Time (JSR-310)

Я изо всех сил пытаюсь настроить «пользовательский» ObjectMapper, который будет использоваться преобразователями Spring Integration DSL. Я получаю java.time.Instant json-представления, которые хочу проанализировать на свойства объекта. то есть:
{"type": "TEST", "source": "TEST", "timestamp": {"epochSecond": 1454503381, "nano": 335000000}}

Сообщение представляет собой сообщение kafka, которое вызывает вопрос: следует ли мне писать настраиваемый сериализатор, реализующий кодеры / декодеры Kafka, чтобы иметь возможность преобразовывать сообщение kafka в нужный объект, или весенняя интеграция должна делать это автоматически?

fw / dependencies и версия:
Spring Boot - 1.3.2.RELEASE
Spring Integration Java Dsl - 1.1.1.RELEASE
FasterXml Jackson - 2.6.5

Я добавил эту конфигурацию Java в проект, следуя документации Джексона: https://github.com/FasterXML/jackson-datatype-jsr310

@Configuration
public class IntegrationConfiguration {

    @Bean
    public JsonObjectMapper<JsonNode, JsonParser> jsonObjectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        return new Jackson2JsonObjectMapper(mapper);
    }
}

а также следующий артефакт Jackson JSR-310:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.6.5</version>
</dependency>

Основываясь на этом сообщении в блоге Spring, мне даже не нужно регистрировать новый модуль времени Java8. https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring#jackson-modules

Это мое исключение:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class java.time.Instant]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: {"type":"TEST","source":"TEST","timestamp":{"epochSecond":1454503381,"nano":335000000}}; line: 1, column: 71] (through reference chain: my.app.MyDto["timestamp"])
    at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1106)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:296)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:133)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:520)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:95)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:258)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:125)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3736)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2764)
    at org.springframework.integration.support.json.Jackson2JsonObjectMapper.fromJson(Jackson2JsonObjectMapper.java:75)
    at org.springframework.integration.support.json.Jackson2JsonObjectMapper.fromJson(Jackson2JsonObjectMapper.java:44)
    at org.springframework.integration.support.json.AbstractJacksonJsonObjectMapper.fromJson(AbstractJacksonJsonObjectMapper.java:56)
    at org.springframework.integration.json.JsonToObjectTransformer.doTransform(JsonToObjectTransformer.java:78)
    at org.springframework.integration.transformer.AbstractTransformer.transform(AbstractTransformer.java:33)
    ... 74 more

РЕШЕНИЕ:
Проблема заключалась в том, что я ожидал, что Spring обнаружит архетип jackson-datatype-jsr310 и зарегистрирует JavaTimeModule, но это не так, что совершенно нормально. Есть два способа исправить это:
1. Принятый ответ, если мы используем Spring Boot с Spring Integration как есть.
2. Если вы используете Spring Integration Dsl, просто сохраните класс IntegrationConfiguration с jsonObjectMapper () bean и используйте его так:

@Autowired
private JsonObjectMapper jsonObjectMapper;    

return IntegrationFlows
        .from(inboundChannel())
        .transform(Transformers.fromJson(myDto.class, jsonObjectMapper))
        ...

person magiccrafter    schedule 03.02.2016    source источник
comment
перед изменением метки времени на java.time. Мгновенно все работало как шарм. Придется отдельно протестировать компоненты и выявить проблему ...   -  person magiccrafter    schedule 03.02.2016
comment
Я еще не использовал JavaTimeModule, но не могли бы вы убедиться, что действительно используете это jsonObjectMapper() для определения JsonToObjectTransformer? С другой стороны, я не вижу никаких тестов для такого вида Instant представления: github.com/FasterXML/jackson-datatype-jsr310/blob/master/src/   -  person Artem Bilan    schedule 03.02.2016
comment
Я решил проблему и получил следующие результаты. По какой-то причине JavaTimeModule не принимается во внимание. Вторая проблема заключается в том, что я тестировал json-представление java.time.Instant по умолчанию, и оно другое (правильно: timestamp: 1454514333.855000000).   -  person magiccrafter    schedule 03.02.2016


Ответы (2)


Нет ничего общего с Spring Boot в этом вопросе, чтобы заставить Spring Integration использовать это.

Вам просто нужно настроить JsonToObjectTransformer таким образом, чтобы ваш jsonObjetMapper():

@Bean
@Transformer(inputChannel="input", outputChannel="output")
JsonToObjectTransformer jsonToObjectTransformer() {
    return new JsonToObjectTransformer(jsonObjectMapper());
}

Хотя нет смысла регистрировать JsonObjectMapper как bean.

person Artem Bilan    schedule 03.02.2016
comment
Хороший. В 10 раз больше. Я попытался определить jsonToObjectTransformer, но не знал об аннотации @Transformer. 10x снова - person magiccrafter; 03.02.2016
comment
Что ж, смотрите больше в Справочном руководстве по интеграции Spring: docs.spring.io/spring-integration/docs/latest-ga/reference/html/ и обратите внимание на Spring Integration Java DSL: github.com/spring-projects/spring-integration-java-dsl - person Artem Bilan; 03.02.2016

Вы определили кодировщик для своего адаптера канала?

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

В вашем случае StringEncoder должен решить проблему.

<bean id="myEncoder" class="org.springframework.integration.kafka.serializer.common.StringEncoder"/>
person Lucas Cegatti    schedule 03.02.2016
comment
Я думаю, это неважно. Он получает обычную строку JSON от Kafka и пытается десериализовать ее в POJO, но терпит неудачу с проблемой Instant ctor. - person Artem Bilan; 03.02.2016