You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Quarkus中使用Kotlin序列化反序列化对象列表时遇到的问题

Quarkus中使用Kotlin序列化反序列化对象列表时遇到的问题

这个问题我之前也碰到过,核心原因是Quarkus早期版本的kotlin-serialization集成扩展,对顶层泛型集合(比如List<Data>)的反序列化逻辑有缺陷——它错误地把顶层List当成多态类型来解析,而不是直接调用List的序列化器处理,这就导致了你看到的Polymorphic<List>相关的解码异常。

虽然Kotlin Serialization本身完全支持顶层List的反序列化(你用Json.decodeFromString测试正常就是最好的证明),但Quarkus的集成层在处理REST请求体时,因为泛型类型擦除的问题,没有正确把List的具体元素类型传递给序列化器,从而触发了错误的多态解析逻辑。

下面给你几个解决方案,按推荐程度排序:

方案1:升级Quarkus版本到修复该问题的版本

Quarkus团队已经在3.10.0版本中彻底修复了这个顶层集合的反序列化问题。如果你当前用的Quarkus版本低于3.10.0,直接升级到最新稳定版,你的原始代码不需要做任何修改,就能和Jackson一样正常接收顶层List的请求体。

方案2:临时封装请求体(无需升级版本,需调整客户端)

如果暂时没办法升级Quarkus,可以把顶层List封装成一个序列化的数据类,让请求体从纯数组变成对象包裹数组的形式:

// 新增一个封装类
@Serializable
data class DataRequest(val items: List<Data>)

@Path("/")
class MyService {
    // 修改接口接收封装类
    @POST
    fun accept(request: DataRequest): Int {
        return request.items.count()
    }
}

对应的请求体需要调整为:

{
  "items": [{"number":773211},{"number":562867}]
}

这个方法不需要改动框架依赖,但需要客户端配合修改请求格式,适合暂时无法升级框架的场景。

方案3:自定义MessageBodyReader(不修改客户端,无需升级)

如果不想调整客户端请求格式,也可以自定义一个MessageBodyReader来专门处理List<Data>的反序列化,绕过Quarkus默认的有问题的逻辑:

import jakarta.ws.rs.Consumes
import jakarta.ws.rs.core.MediaType
import jakarta.ws.rs.core.MultivaluedMap
import jakarta.ws.rs.ext.MessageBodyReader
import jakarta.ws.rs.ext.Provider
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import java.io.InputStream
import java.lang.reflect.Type
import java.nio.charset.StandardCharsets

@Provider // 标记为Quarkus自动扫描的扩展组件
@Consumes(MediaType.APPLICATION_JSON)
class DataListBodyReader : MessageBodyReader<List<Data>> {

    // 判断当前请求体是否是我们要处理的List<Data>类型
    override fun isReadable(
        type: Class<*>,
        genericType: Type,
        annotations: Array<out Annotation>,
        mediaType: MediaType
    ): Boolean {
        // 这里替换成你的Data类的完整包路径
        return genericType.typeName == "java.util.List<com.yourpackage.Data>"
    }

    // 手动执行Kotlin Serialization的反序列化逻辑
    override fun readFrom(
        type: Class<List<Data>>,
        genericType: Type,
        annotations: Array<out Annotation>,
        mediaType: MediaType,
        httpHeaders: MultivaluedMap<String, String>,
        entityStream: InputStream
    ): List<Data> {
        val jsonContent = String(entityStream.readAllBytes(), StandardCharsets.UTF_8)
        return Json.decodeFromString(jsonContent)
    }
}

把这个类放在你的Quarkus应用的代码路径下,Quarkus会自动扫描并注册这个阅读器,你的原始接口代码(直接接收List<Data>)就能正常工作了,客户端也不需要做任何修改。

补充说明:你提到用Jackson能正常工作,是因为Jackson的Quarkus集成很早就完善处理了顶层集合的类型擦除问题,而Kotlin Serialization的Quarkus集成是后来才跟上的,所以旧版本会有这个兼容性问题。

内容来源于stack exchange

火山引擎 最新活动