KMM项目中Ktor解析API JSON响应触发NoTransformationFoundException
KMM中Ktor解析JSON触发
NoTransformationFoundException问题 问题描述
在KMM项目中使用Ktor框架解析API返回的JSON响应时,总是触发io.ktor.client.call.NoTransformationFoundException异常,但将响应转为String类型时可正常工作。
请求方法代码
suspend fun getSkoolURL22(code:String, token:String): GetSchoolResponse { return clientv.submitForm( url = "$CONST_SCHOOL_VERIFICATION_URL.", formParameters = Parameters.build { append("token", token) append("skool_code", code) }).body() }
HttpClient配置
val clientv = HttpClient() { install(Logging) { logger = Logger.SIMPLE level = LogLevel.ALL } install(ContentNegotiation) { json(Json { prettyPrint = true isLenient = true ignoreUnknownKeys = true }) } }
数据类定义
@Serializable data class GetSchoolResponse( @Serializable val code: Int, @Serializable val data: List<Data>, @Serializable val message: String) { @Serializable data class Data( @Serializable val domain: String, @Serializable val id: Int, @Serializable val ip: String, @Serializable val sk_code: Int, @Serializable val sk_name: String) }
注:内部嵌套的
Data类无需重复添加@Serializable注解,外层类的注解已可覆盖嵌套类
异常信息
io.ktor.client.call.NoTransformationFoundException: No transformation found: class io.ktor.utils.io.ByteBufferChannel (Kotlin reflection is not available) -> class com.example.skoolkmm.data.GetSchoolResponse (Kotlin reflection is not available) status: 200 OK response headers: content-type: text/html; charset=UTF-8 , date: Sun, 11 Jun 2023 21:21:11 GMT , server: Apache , transfer-encoding: chunked , vary: Accept-Encoding , x-iplb-instance: 38231 , x-iplb-request-id: C4B34E62:51E4_36245B3E:0050_64863AC7_0DE2:25792 , x-powered-by: PHP/7.0
服务器响应内容
2023-06-11 22:21:10.229 19455-19511/com.example.skoolkmm.android I/System.out: -> content-type: text/html; charset=UTF-8 2023-06-11 22:21:10.229 19455-19511/com.example.skoolkmm.android I/System.out: -> date: Sun, 11 Jun 2023 21:21:11 GMT 2023-06-11 22:21:10.229 19455-19511/com.example.skoolkmm.android I/System.out: -> server: Apache 2023-06-11 22:21:10.229 19455-19511/com.example.skoolkmm.android I/System.out: -> transfer-encoding: chunked 2023-06-11 22:21:10.229 19455-19511/com.example.skoolkmm.android I/System.out: -> vary: Accept-Encoding 2023-06-11 22:21:10.229 19455-19511/com.example.skoolkmm.android I/System.out: -> x-iplb-instance: 38231 2023-06-11 22:21:10.229 19455-19511/com.example.skoolkmm.android I/System.out: -> x-iplb-request-id: C4B34E62:51E4_36245B3E:0050_64863AC7_0DE2:25792 2023-06-11 22:21:10.229 19455-19511/com.example.skoolkmm.android I/System.out: -> x-powered-by: PHP/7.0 2023-06-11 22:21:10.229 19455-19511/com.example.skoolkmm.android I/System.out: BODY Content-Type: text/html; charset=UTF-8 2023-06-11 22:21:10.229 19455-19511/com.example.skoolkmm.android I/System.out: BODY START 2023-06-11 22:21:10.230 19455-19511/com.example.skoolkmm.android I/System.out: {"code":200,"message":"success","data":[{"id":1,"domain":"https://dev.vyzyoschool.com/","ip":"197.13.17.67","sk_code":111111,"sk_name":"VYZYO"}]} 2023-06-11 22:21:10.230 19455-19511/com.example.skoolkmm.android I/System.out: BODY END
问题原因
从响应头可以看到,服务器返回的content-type是text/html,而Ktor的ContentNegotiation默认仅对application/json类型的响应进行JSON解析。即使响应内容是标准JSON,因为Content-Type不匹配,Ktor无法找到对应的转换器,从而抛出NoTransformationFoundException。
解决方案
方案1:修改ContentNegotiation配置,支持text/html类型解析
在安装ContentNegotiation时,为JSON转换器指定额外的ContentType为text/html:
val clientv = HttpClient() { install(Logging) { logger = Logger.SIMPLE level = LogLevel.ALL } install(ContentNegotiation) { json(Json { prettyPrint = true isLenient = true ignoreUnknownKeys = true }, contentType = ContentType.Text.Html) // 添加此行,支持text/html类型的JSON解析 } }
方案2:请求时手动指定解析类型
在调用body()方法时,明确指定使用JSON解析并适配text/html类型:
suspend fun getSkoolURL22(code:String, token:String): GetSchoolResponse { return clientv.submitForm( url = "$CONST_SCHOOL_VERIFICATION_URL.", formParameters = Parameters.build { append("token", token) append("skool_code", code) }).body<GetSchoolResponse>(JsonReceiveContent(ContentType.Text.Html)) }
方案3(可选):优化数据类注解
内部嵌套的Data类无需重复添加@Serializable注解,外层GetSchoolResponse的注解已可覆盖嵌套类,简化后的数据类如下:
@Serializable data class GetSchoolResponse( val code: Int, val data: List<Data>, val message: String) { data class Data( val domain: String, val id: Int, val ip: String, val sk_code: Int, val sk_name: String) }
内容的提问来源于stack exchange,提问作者Big Boss
相关产品推荐
相关产品推荐

