SQS消息解析失败:Jackson解析异常与空指针错误排查
问题排查与解决方案
问题描述
向SQS发送消息时,解析阶段抛出JsonParseException:
{"message":"Exception com.fasterxml.jackson.core.JsonParseException: Unexpected character ('i' (code 105)): was expecting comma to separate Array entries\n at [Source: (String)\
后续触发空指针异常,无法将sqsEvent.payload转换为非空类型TriggerPayoutPayload,导致payoutProcessor.process()方法无法执行,仅打印日志。
类结构
data class TriggerPayoutPayload( val customerId: String, val bookingType: BookingEntityType, val itemsId: List<ListItems>?, ) enum class BookingEntityType { PREBOOKED, RETURN, } enum class ItemType { HEALTH, BEVERAGE, } data class ListItems( val itemType: ItemType, val description: List<String>, )
发送的Payload
{ "type": "test.payout", "id":"sqsMsgId-01-03-01", "body": { "customer_id": "cus123", "booking_type": "PREBOOKED", "items_id":["{\"item_type\":\"HEALTH\",\"description\":\"Healthy\"}"] } }
SQS事件处理代码
private fun eventsProcessor(sqsEvent: SqsEvent<*>) { log.info("Handling event ${sqsEvent.id} type: ${sqsEvent.type}") when (sqsEvent.type) { PREBOOKED -> payoutProcessor.process( sqsEvent.payload as TriggerPayoutPayload, ) } data class SqsEvent<T>( val id: String, val time: Instant? = null, val type: String, @JsonSubTypes( Type(value = TriggerPayoutPayload::class, name = "PREBOOKED") ) val payload: T? = null, val metadata: EventMetadata? = null, var messageId: String? = null, var receiptHandler: String? = null )
(注:原代码中PREBOOKED -应为PREBOOKED ->,已修正语法错误)
堆栈跟踪
{"message":"Exception java.lang.NullPointerException: null cannot be cast to non-null type com.testproject.checkout.britania.migration.domain.TriggerPayoutPayload, stacktrace: java.lang.NullPointerException: null cannot be cast to non-null type com.testproject.checkout.britania.migration.domain.TriggerPayoutPayload\n\tat com.testproject.checkout.britania.migration.ApplicationHandler.eventsProcessor(ApplicationHandler.kt:114)\n\tat com.testproject.checkout.britania.migration.ApplicationHandler.handleRequest(ApplicationHandler.kt:72)\n\tat com.testproject.checkout.britania.migration.ApplicationHandler.handleRequest(ApplicationHandler.kt:35)\n\tat com.amazonaws.services.lambda.runtime.api.client.EventHandlerLoader$PojoHandlerAsStreamHandler.handleRequest(EventHandlerLoader.java:202)\n\tat com.amazonaws.services.lambda.runtime.api.client.EventHandlerLoader$2.call(EventHandlerLoader.java:905)\n\tat com.amazonaws.services.lambda.runtime.api.client.AWSLambda.startRuntime(AWSLambda.java:245)\n\tat com.amazonaws.services.lambda.runtime.api.client.AWSLambda.startRuntime(AWSLambda.java:197)\n\tat
问题原因分析
- Payload格式错误:
items_id数组中的元素是字符串化的JSON(带转义符),而非直接的JSON对象。Jackson解析时期望数组条目是对象,却读取到字符串内容,导致解析失败;同时description字段在数据类中是List<String>,但Payload中是单个字符串"Healthy",不符合类型要求。 - 消息结构不匹配:SQS消息中的
body字段在SqsEvent类中不存在,SqsEvent定义的是payload字段,导致Jackson无法将body内容映射到payload属性。 - 类型映射不匹配:
SqsEvent的@JsonSubTypes注解中指定TriggerPayoutPayload对应的name是"PREBOOKED",但发送的Payload中type字段值是"test.payout",Jackson无法匹配类型,导致payload解析为null。 - 非空强制转换:代码中使用
as TriggerPayoutPayload(非空强制转换),当payload为null时直接抛出空指针异常。 - 字段命名不匹配:Payload中使用下划线命名(如
customer_id),而Kotlin数据类使用驼峰命名(如customerId),默认Jackson不会自动转换,导致字段映射失败。
解决方案
修正Payload格式与结构
调整消息结构,去掉body字段,直接使用payload映射;修正items_id为JSON对象数组,description改为字符串数组:{ "type": "PREBOOKED", "id": "sqsMsgId-01-03-01", "payload": { "customer_id": "cus123", "booking_type": "PREBOOKED", "items_id": [ { "item_type": "HEALTH", "description": ["Healthy"] } ] } }统一类型映射标识
确保Payload中的type值与@JsonSubTypes注解的name一致:要么将Payload的type改为"PREBOOKED",要么修改注解的name为"test.payout"。处理字段命名差异
两种方式任选其一:- 在数据类字段添加
@JsonProperty注解,指定对应JSON字段名:data class TriggerPayoutPayload( @JsonProperty("customer_id") val customerId: String, @JsonProperty("booking_type") val bookingType: BookingEntityType, @JsonProperty("items_id") val itemsId: List<ListItems>?, ) data class ListItems( @JsonProperty("item_type") val itemType: ItemType, val description: List<String>, ) - 配置Jackson全局支持下划线转驼峰(如在Spring环境中添加配置类)。
- 在数据类字段添加
安全处理空值与类型转换
替换非空强制转换为安全转换,避免空指针异常:private fun eventsProcessor(sqsEvent: SqsEvent<*>) { log.info("Handling event ${sqsEvent.id} type: ${sqsEvent.type}") when (sqsEvent.type) { "PREBOOKED" -> { val payload = sqsEvent.payload as? TriggerPayoutPayload if (payload != null) { payoutProcessor.process(payload) } else { log.error("Invalid or null payload for event type PREBOOKED") } } } }
内容的提问来源于stack exchange,提问作者Rahul Bhandari
相关产品推荐
相关产品推荐

