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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 10:54:54