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

如何使用Jackson解析Kotlin双泛型密封类Response?

双泛型密封类Jackson解析问题的解决方案

问题描述

定义了用于表示网络响应状态的双泛型密封类Response,代码如下:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "status")
@JsonSubTypes(
    value = [
        JsonSubTypes.Type(value = Response.OK::class, name = "OK"),
        JsonSubTypes.Type(value = Response.Error::class, name = "ERROR"),
    ]
)
sealed class Response<out Content, out Error> {
    data class OK<out Content>(val content: Content) : Response<Content, Nothing>()
    data class Error<out Error>(val error: Error) : Response<Nothing, Error>()
}

网络响应JSON根据status字段区分结构:

  • status="OK"时包含content字段,对应Response.OK
  • status="ERROR"时包含error字段,对应Response.Error

但解析时抛出错误:

Could not resolve type id 'OK' as a subtype of `com.example.models.Response<java.lang.String,java.lang.String>`: Failed to specialize base type com.example.models.Response<java.lang.String,java.lang.String> as com.example.models.Response$OK, problem: Type parameter #1/2 differs; can not specialize java.lang.String with java.lang.Object
 at [Source: (String)"{
  "status": "OK",
  "content": "Hello"
}"; line: 2, column: 13]

错误原因

Jackson解析子类型时无法协变匹配父类的双泛型参数:

  • Response.OK继承的是Response<Content, Nothing>,但解析时指定的目标类型是Response<String, String>,第二个泛型参数不匹配(Nothing vs String)
  • 类型擦除加上协变泛型的特性,导致Jackson无法完成父类到子类的类型特化

解决方案

方案1:调整子类泛型声明,对齐父类双泛型

修改子类的泛型定义,让每个子类都携带父类的两个泛型参数(即使其中一个用Nothing占位),确保Jackson能正确匹配类型参数:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "status")
@JsonSubTypes(
    value = [
        JsonSubTypes.Type(value = Response.OK::class, name = "OK"),
        JsonSubTypes.Type(value = Response.Error::class, name = "ERROR"),
    ]
)
sealed class Response<out Content, out Error> {
    data class OK<out Content, out Error>(val content: Content) : Response<Content, Nothing>()
    data class Error<out Content, out Error>(val error: Error) : Response<Nothing, Error>()
}

方案2:使用TypeReference明确指定泛型类型

解析时用TypeReference替代reified泛型,避免类型擦除导致的泛型信息丢失:

@Test
fun `parses OK response`() {
    val json = """
    {
      "status": "OK",
      "content": "Hello"
    }
    """.trimIndent()

    val result = objectMapper.readValue(json, object : TypeReference<Response<String, String>>() {})
    when (result) {
        is Response.OK -> assertEquals(result.content, "Hello")
        is Response.Error -> fail()
    }
}

方案3:关闭Jackson严格子类型检查(不推荐)

临时关闭FAIL_ON_INVALID_SUBTYPE配置绕过类型检查,但可能隐藏其他类型问题:

val objectMapper = ObjectMapper().registerModule(KotlinModule())
objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false)

测试验证

修改后重新运行测试代码,即可正常解析OK和ERROR两种类型的JSON响应。

内容的提问来源于stack exchange,提问作者Jan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 22:56:29