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




