Servicio Spring RESET de arranque: la deserialization de JSON no funciona

Estoy trabajando en un service REST usando spring-boot y Kotlin. (Debo mencionar que es la primera vez que uso ambos). Tengo problemas para lograr que Jackson deserialice JSON de una request POST utilizando este código:

@RequestMapping("cloudservice/login/{uuid}", method = arrayOf(RequestMethod.POST)) fun login(@PathVariable(value="uuid")uuid: String, @RequestBody user: CloudServiceUser ) : ResponseEntity<CloudServiceUser> { val cloudServiceFactory : Class<CloudServiceFactory> = cloudServiceRepository.cloudServiceExtensions[UUID.fromString(uuid)] ?: throw InvalidPathVariableException("Invalid UUID.") var token : String try { token = cloudServiceFactory.newInstance().authenticationService.login(user.userId, user.password) } catch (e:Exception ){ throw CloudServiceException(e.message) } return ResponseEntity(CloudServiceUser(userId=user.userId, password = "", token = token),HttpStatus.OK) } 

El object de usuario es simplemente:

 data class CloudServiceUser(val userId: String, val password:String, val token:String) 

Me sale el error

 org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Can not construct instance of com.irotsoma.cloudbackenc.cloudservice.CloudServiceUser: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?) at [Source: java.io.PushbackInputStream@682b15d6; line: 1, column: 2]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.irotsoma.cloudbackenc.cloudservice.CloudServiceUser: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?) at [Source: java.io.PushbackInputStream@682b15d6; line: 1, column: 2] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:229) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:213) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:197) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:147) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:125) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:99) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:832) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:743) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:869) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:648) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) [spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:261) [spring-boot-actuator-1.3.5.RELEASE.jar:1.3.5.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:115) [spring-boot-actuator-1.3.5.RELEASE.jar:1.3.5.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103) [spring-boot-actuator-1.3.5.RELEASE.jar:1.3.5.RELEASE] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502) [tomcat-embed-core-8.0.33.jar:8.0.33] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458) [tomcat-embed-core-8.0.33.jar:8.0.33] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_92] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_92] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.0.33.jar:8.0.33] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_92] Caused by: com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.irotsoma.cloudbackenc.cloudservice.CloudServiceUser: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?) at [Source: java.io.PushbackInputStream@682b15d6; line: 1, column: 2] at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:261) ~[jackson-databind-2.8.0.jar:2.8.0] at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1420) ~[jackson-databind-2.8.0.jar:2.8.0] at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1011) ~[jackson-databind-2.8.0.jar:2.8.0] at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1201) ~[jackson-databind-2.8.0.jar:2.8.0] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:314) ~[jackson-databind-2.8.0.jar:2.8.0] at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:148) ~[jackson-databind-2.8.0.jar:2.8.0] at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3789) ~[jackson-databind-2.8.0.jar:2.8.0] at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2913) ~[jackson-databind-2.8.0.jar:2.8.0] at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:226) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE] ... 66 common frames omitted 

Sin embargo, la serialization en la statement de devolución funciona bien para serializar a un object JSON y envía la respuesta. Es solo la deserialization que no está funcionando.

Si cambio la variable del cuerpo a una Cadena y creo mapeador manualmente, también funciona bien. Me gusta esto:

 @RequestMapping("cloudservice/login/{uuid}", method = arrayOf(RequestMethod.POST)) fun login(@PathVariable(value="uuid")uuid: String, @RequestBody user: String ) : ResponseEntity<CloudServiceUser> { val mapper = ObjectMapper().registerModule(KotlinModule()) val mapperData: CloudServiceUser = mapper.readValue(user) val cloudServiceFactory : Class<CloudServiceFactory> = cloudServiceRepository.cloudServiceExtensions[UUID.fromString(uuid)] ?: throw InvalidPathVariableException("Invalid UUID.") var token : String try { token = cloudServiceFactory.newInstance().authenticationService.login(mapperData.userId, mapperData.password) } catch (e:Exception ){ throw CloudServiceException(e.message) } return ResponseEntity(CloudServiceUser(userId=mapperData.userId, password = "", token = token),HttpStatus.OK) } 

¿Algunas ideas?

Es un proyecto de código abierto, así que siéntase libre de navegarlo si necesita más información: https://github.com/irotsoma/cloudbackenc . Tenga en count que está en un estado muy temprano en este momento.

EDITAR: Solo quería agregar una nota sobre mis problemas con las testings de integración como se indica en los comentarios sobre la respuesta aceptada. Terminé nunca obteniendo la testing de integración para poder deserializar la respuesta (aunque el lado del controller REST funcionó bien al igual que en la producción). Así que encontré una solución usando un object Map, que por alguna razón fue capaz de deserializar, y luego procesarlo manualmente en lugar de usar el object de class. Ya que es solo un problema con la testing y no el código que se testing, esto estuvo bien para mí.

 val returnValue = template.postForEntity("REQUEST URL", httpEntity, Map::class.java) 

Debes ayudar a Jackson a deserializar las classs de datos como se indica en el post de error. Puede hacerlo registrando jackson-module-kotlin en ObjectMapper como sigue :

 val mapper = ObjectMapper().registerModule(KotlinModule()) 

Para decirle a la spring sobre su propio ObjectMapper personalizado, defina un @Bean y @Primary como @Primary :

 @Configuration open class ObjectMapperConfiguration { @Bean @Primary open fun objectMapper() = ObjectMapper().apply { registerModule(KotlinModule()) } } 

Otras lecturas:

  • Cómo usar jackson para deserializar las collections de Kotlin
  • ¿Cómo deserializo JSON en una list <SomeType> con Kotlin + Jackson?
  • Custom Jersey ExceptionMapper en Spring Boot con kotlin
  • Configure Jackson para usar SNAKE_CASE "globalmente" en Spring Boot 2.0.0.M1
  • Spring Boot Application: File System Watcher para muchas routes
  • El controller de exception Spring Boot @ControllerAdvice no se activa
  • Estado de respuesta HTTP SpringBoot Kotlin Api
  • Jackson ready only collections
  • Analizando un object json con un campo dynamic en Kotlin
  • Encabezados HTTP no devueltos en EC2
  • No se puede conectar a Remote Mongo DB mediante Springboot
  • ¿Cómo habilitar la authentication de portador en la aplicación Spring Boot?
  • Enfoque correcto para la class de Kotlin inyectada de constructor inmutable