rekire
rekire

Reputation: 47975

ktor throws for some data classes an NoTransformationFoundException

I have a feature branch where I'm loading data from a rest service. For two requests this works absolutely as expected, but for one request I get always an NoTransformationFoundException.

I'm creating the http client like this:

private val httpClient =
    HttpClient(OkHttp) {
        install(JsonFeature) {
            serializer = GsonSerializer()
        }
    }

Above you see I'm using okhttp with gson. Just to make the issue easier to debug here are my ktor dependencies:

compile "io.ktor:ktor-client-okhttp:1.2.6"
compile "io.ktor:ktor-client-json:1.2.6"
compile "io.ktor:ktor-client-gson:1.2.6"

The request like below is works fine for me. Here is the relevant documentation (including response JSON):

data class Vendors(val vendors: List<Vendor>)

data class Vendor(
    val id: String,
    val name: String,
    val roles: List<String>
)

fun fetchVendors() = runBlocking {
    httpClient.get<Vendors>("https://api.amazonalexa.com/v1/vendors") {
        headers.append("Authorization", "Bearer $accessToken")
    }?.vendors
}

Update: I knew there had to be a difference: The content type above is application/json

However this call (fetching the skills, with the documentation here) keeps failing:

data class Skills(
    val isTruncated: Boolean,
    val skills: List<Skill>
)

data class Skill(
    val apis: List<String>,
    val asin: String,
    val lastUpdated: String,
    val nameByLocale: Map<String, String>,
    val publicationStatus: String,
    val skillId: String,
    val stage: String
)

fun fetchSkills(vendor: String) = runBlocking {
    httpClient.get<Skills>("https://api.amazonalexa.com/v1/skills?vendorId=$vendor") {
        headers.append("Authorization", "Bearer $accessToken")
    }.skills
}

Update: Here is the response Content-Type: application/json+hal

I don't understand why one works while the other fails with this exception:

Exception in thread "main" io.ktor.client.call.NoTransformationFoundException: No transformation found: class kotlinx.coroutines.io.ByteBufferChannel -> class my.package.name.Skills
        at io.ktor.client.call.HttpClientCall.receive(HttpClientCall.kt:88)
        at my.package.name.AmazonApi$fetchSkills$1.invokeSuspend(AmazonApi.kt:200)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(PipelineContext.kt:215)
        at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:172)
        at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
        at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(PipelineContext.kt:122)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
        at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(PipelineContext.kt:215)
        at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:172)
        at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
        at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(PipelineContext.kt:122)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
        at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(PipelineContext.kt:215)
        at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:172)
        at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
        at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(PipelineContext.kt:122)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
        at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
        at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:270)
        at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:79)
        at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:54)
        at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
        at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:36)
        at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
        at my.package.name.AmazonApi.fetchSkills(AmazonApi.kt:131)
        at my.package.name.Cli.createProject(Cli.kt:305)
        at my.package.name.Cli.parseArgs(Cli.kt:208)
        at my.package.name.Cli$Companion.main(Cli.kt:508)
        at my.package.name.Cli.main(Cli.kt)

Please give me some hints how to resolve this issue, or at least how I can debug it. Btw when I use String instead of Skill I see the expected response JSON.

Upvotes: 5

Views: 10490

Answers (2)

Marvin
Marvin

Reputation: 1932

Generally use

implementation("io.ktor", "ktor-client-core", ktorVersion)
implementation("io.ktor", "ktor-client-serialization", ktorVersion)

and then configure the client like this:

HttpClient {
    install(JsonFeature) {
        serializer = KotlinxSerializer()
        // accept(ContentType.Application.Json)
    }
}

The right engine will be chosen automatically. Of course you can use other serializers (like Gson in reikire's answer).

Upvotes: 0

rekire
rekire

Reputation: 47975

Based on a GitHub comment I was able to use this workaround:

val httpClient = HttpClient(OkHttp) {
    install(JsonFeature) {
        serializer = GsonSerializer()
        acceptContentTypes += ContentType("application", "json+hal")
    }
}

However I'm still looking for a better solution without causing warnings.

Upvotes: 2

Related Questions