Estoy usando Kotlin
para escribir un AWS Lambda
. Tengo una clase de datos Kotlin
class MessageObject( val id: String, val name: String, val otherId: String )
Esta clase de datos se utiliza como entrada para la implementación de la interfaz requerida
class Handler : RequestHandler<MessageObject, Output> { ... override fun handleRequest(msg: MessageObject, ctx: Context) { ... } }
Cuando pruebo esta lambda en la consola de aws y le paso un mensaje JSON adecuado, obtengo esto:
An error occurred during JSON parsing: java.lang.RuntimeException java.lang.RuntimeException: An error occurred during JSON parsing Caused by: java.io.UncheckedIOException: com.amazonaws.lambda.thirdparty.com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of 'com.mycode.MessageObject'(no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
Estoy casi seguro de que esto se soluciona diciendo: ObjectMapper().registerModule(KotlinModule())
pero en el mundo de AWS Lambda
, ¿cómo edito el mapeador de objetos proporcionado por AWS?
Si no ha logrado que funcione con KotlinModule, ya que el problema que tiene es que Jackson requiere un constructor vacío predeterminado y actualmente no tiene uno. Simplemente podría cambiar su MessageObject de la siguiente manera y debería funcionar:
data class MessageObject( var id: String = "", var name: String = "", var otherId: String = "" )
Creé este repositorio con una plantilla lambda de kotlin completamente funcional utilizando Serverless Framework. Eche un vistazo a otros datos que pueda necesitar: https://github.com/crafton/sls-aws-lambda-kotlin-gradlekt
Desafortunadamente, no puede usar la clase de datos con RequestHandler<I, O>
provisto, porque necesita registrar el módulo kotlin para su mapeador Jackson para trabajar con clases de datos. Pero puede escribir su propio RequestHandler, al que le gustará este.
Aquí está el código:
interface MyRequestStreamHandler<I : Any, O : Any?> : RequestStreamHandler { val inputType: Class<I> fun handleRequest(input: I, context: Context): O? override fun handleRequest(inputStream: InputStream, outputStream: OutputStream, context: Context) { handleRequest(inputStream.readJson(inputType), context).writeJsonNullable(outputStream) } interface MessageObjectRequestHandler : MyRequestStreamHandler< MessageObject, Output> { override val inputType: Class<MessageObject > get() = MessageObject::class.java } }
Y jackson útil:
private val objectMapper = jacksonObjectMapper() .configure(JsonParser.Feature.ALLOW_COMMENTS, true) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .registerKotlinModule() private val writer: ObjectWriter = objectMapper.writer() fun <T : Any> readJson(clazz: Class<T>, stream: InputStream): T = objectMapper.readValue(stream, clazz) fun <T : Any> InputStream.readJson(clazz: Class<T>): T = readJson(clazz, this) fun Any?.writeJsonNullable(outputStream: OutputStream) { if (this != null) writer.writeValue(outputStream, this) }
Ahora, puede mantener su clase MessageObject como clase de datos, y su controlador se verá así:
class LambdaMain : MessageObjectRequestHandler { override fun handleRequest(input: MessageObject, context: Context): Output { //... } }