使用Ktor将JSON反序列化为Kotlin Map/数据类时遇转换错误
Ktor JSON反序列化报错:NoTransformationFoundException
问题描述
使用Ktor将OAuth2接口返回的JSON反序列化为Map<String, Any>或自定义数据类时,持续触发NoTransformationFoundException,提示无法完成ByteBufferChannel到目标类型的转换。
初始代码(反序列化为Map)
val beans = beans { bean("oauthClient") { HttpClient(CIO) { expectSuccess = true install(DefaultRequest){ url("${env["oauth.url"]!!}/ms_oauth/oauth2/endpoints/oauthservice/tokens") } install(Auth) { basic { credentials { BasicAuthCredentials(username = env["client.id"]!!, password = env["client.secret"]!!) } } } install(ContentNegotiation) { json() } } } bean { val oauthResults: Map<String,Any> = runBlocking { // 此处报错 ref<HttpClient>("oauthClient").submitForm("${env["oauth.url"]!!}/ms_oauth/oauth2/endpoints/oauthservice/tokens", Parameters.build { append("grant_type", "password") append("scope", "bug.rest.public") append("username", env["ssoUsername"]!!) append("password", env["ssoPassword"]!!) } ).body() } } }
初始错误信息
Caused by: io.ktor.client.call.NoTransformationFoundException: No transformation found: class io.ktor.utils.io.ByteBufferChannel -> class kotlin.collections.Map
更新尝试(反序列化为自定义数据类)
改用标记@Serializable的数据类后,仍报同类错误:
@Serializable data class Oauth constructor( @SerialName("expires_in") val expiration: Int, @SerialName("token_type") val tokenType:String, @SerialName("access_token") val accessToken: String )
更新后的错误信息
io.ktor.client.call.NoTransformationFoundException: No transformation found: class io.ktor.utils.io.ByteBufferChannel -> class com.company.Oauth
解决方法
核心原因
submitForm默认发送application/x-www-form-urlencoded格式请求,但OAuth2 token端点返回的JSON响应可能未被Ktor正确识别——要么是响应头的Content-Type未标记为application/json,要么是ContentNegotiation配置不完整,导致JSON反序列化逻辑未触发。
具体修复步骤
确认依赖齐全
确保项目已引入Ktor JSON序列化相关依赖(以Gradle Kotlin DSL为例):implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")完善ContentNegotiation配置
给json()添加必要的序列化规则,比如忽略未知字段(避免响应有额外字段时失败):install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true isLenient = true }) }显式指定反序列化类型
调用body()时明确指定目标类型,确保Ktor触发JSON转换:val oauthResults: Oauth = runBlocking { ref<HttpClient>("oauthClient").submitForm( url = "${env["oauth.url"]!!}/ms_oauth/oauth2/endpoints/oauthservice/tokens", formParameters = Parameters.build { append("grant_type", "password") append("scope", "bug.rest.public") append("username", env["ssoUsername"]!!) append("password", env["ssoPassword"]!!) } ).body<Oauth>() }排查响应状态与内容
暂时注释expectSuccess = true,打印响应状态码和原始内容,确认请求是否成功、响应是否为有效JSON:val response = ref<HttpClient>("oauthClient").submitForm(...) println("Response status: ${response.status}") println("Response content: ${response.readText()}")
验证修复
修改后重新运行代码,确认能正常将响应反序列化为目标类型。
内容的提问来源于stack exchange,提问作者Christian Bongiorno
相关产品推荐
相关产品推荐

