You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 

问题原因分析

  1. Payload格式错误:items_id数组中的元素是字符串化的JSON(带转义符),而非直接的JSON对象。Jackson解析时期望数组条目是对象,却读取到字符串内容,导致解析失败;同时description字段在数据类中是List<String>,但Payload中是单个字符串"Healthy",不符合类型要求。
  2. 消息结构不匹配:SQS消息中的body字段在SqsEvent类中不存在,SqsEvent定义的是payload字段,导致Jackson无法将body内容映射到payload属性。
  3. 类型映射不匹配:SqsEvent的@JsonSubTypes注解中指定TriggerPayoutPayload对应的name是"PREBOOKED",但发送的Payload中type字段值是"test.payout",Jackson无法匹配类型,导致payload解析为null。
  4. 非空强制转换:代码中使用as TriggerPayoutPayload(非空强制转换),当payload为null时直接抛出空指针异常。
  5. 字段命名不匹配:Payload中使用下划线命名(如customer_id),而Kotlin数据类使用驼峰命名(如customerId),默认Jackson不会自动转换,导致字段映射失败。

解决方案

  1. 修正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"]
          }
        ]
      }
    }
    
  2. 统一类型映射标识
    确保Payload中的type值与@JsonSubTypes注解的name一致:要么将Payload的type改为"PREBOOKED",要么修改注解的name为"test.payout"。

  3. 处理字段命名差异
    两种方式任选其一:

    • 在数据类字段添加@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环境中添加配置类)。
  4. 安全处理空值与类型转换
    替换非空强制转换为安全转换,避免空指针异常:

    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 11:37:05