为啥 ktor 序列化不支持序列化不同元素类型的集合?

Posted

技术标签:

【中文标题】为啥 ktor 序列化不支持序列化不同元素类型的集合?【英文标题】:Why serializing collections of different element types is not supported in ktor-serialization?为什么 ktor 序列化不支持序列化不同元素类型的集合? 【发布时间】:2021-01-26 20:11:19 【问题描述】:

我正在尝试制作一个简单的服务器,它以 JSON 格式提供序列化列表。待序列化的List就是the official blog post的多态序列化部分的例子。

但是使用 ktor 的序列化功能,我得到以下异常。

21:53:25.536 [nioEventLoopGroup-4-1] ERROR ktor.application - Unhandled: GET - /
java.lang.IllegalStateException: Serializing collections of different element types is not yet supported. Selected serializers: [DirectMessage, BroadcastMessage]
    at io.ktor.serialization.SerializerLookupKt.elementSerializer(SerializerLookup.kt:71)

既然密封类是选择 Kotlin 的一个关键特性,我真的很奇怪为什么不支持它。

ktor-serialization 是否有充分的理由不支持这一点?或者我应该发布一个从SerializerLookup.kt 删除此检查的问题吗?


我通过在 IntelliJ 中选择 New Project > Kotlin > Application 来编写此代码。修改后的代码如下所示。

我的服务器.kt:

import io.ktor.application.*
import io.ktor.features.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.serialization.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import kotlinx.serialization.Serializable

@Serializable
sealed class Message 
    abstract val content: String


@Serializable
data class BroadcastMessage(override val content: String) : Message()

@Serializable
data class DirectMessage(override val content: String, val recipient: String) : Message()

val data: List<Message> = listOf(
    DirectMessage("Hey, Joe!", "Joe"),
    BroadcastMessage("Hey, all!")
)

fun main() 
    embeddedServer(Netty, port = 8080, host = "127.0.0.1") 
        install(ContentNegotiation) 
            json()
        
        routing 
            get("/") 
                call.respond(data)
            
        
    .start(wait = true)

我的 build.gradle.kts:

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins 
    kotlin("jvm") version "1.4.10"
    application
    kotlin("plugin.serialization") version "1.4.10"

group = "com.example.ktor.serialization"
version = "1.0-SNAPSHOT"

repositories 
    mavenCentral()
    jcenter()
    maven 
        url = uri("https://dl.bintray.com/kotlin/ktor")
    
    maven 
        url = uri("https://dl.bintray.com/kotlin/kotlinx")
    

dependencies 
    testImplementation(kotlin("test-junit5"))
    implementation("io.ktor:ktor-server-netty:1.4.1")
    implementation("io.ktor:ktor-html-builder:1.4.1")
    implementation("io.ktor:ktor-serialization:1.4.1")
    implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.7.2")
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.0")
    implementation("ch.qos.logback:logback-classic:1.2.3")

tasks.withType<KotlinCompile>() 
    kotlinOptions.jvmTarget = "11"

application 
    mainClassName = "ServerKt"

【问题讨论】:

【参考方案1】:

正如堆栈跟踪中所说的 this is not supported yet.,所以它可能有一天会到来。

但是,对于这种情况,仍然可以使用解决方法。 问题来自 Ktor,而不是 Kotlinx 序列化。 因此,您可以将数据序列化为 JSON,然后将它们作为响应发送,如下所示:

fun Application.module(testing: Boolean = false) 
    install(ContentNegotiation)  json() 

    routing 
        get("/") 
            val data: List<Message> = listOf(
                DirectMessage("Hey, Joe!", "Joe"),
                BroadcastMessage("Hey, all!")
            )

            val string = Json.encodeToString(data)
            call.respondText(string, contentType = ContentType.Application.Json)
        
    


@Serializable
sealed class Message 
    abstract val content: String


@Serializable
data class BroadcastMessage(override val content: String) : Message()

@Serializable
data class DirectMessage(override val content: String, val recipient: String) : Message()

【讨论】:

【参考方案2】:

原因是我们没有具体的类型信息,只能在运行时分析实例类。分析和运行时类型的交集并不是一件容易的事,而且肯定会非常低效,这在服务器端是不可接受的。

使用typeOf 可能会有所帮助,但我们尚未分析此类更改(包括分配配置文件)对性能的影响。另一个原因是我们不知道 typeOf(它不存在),而 call.respond 的设计没有它,所以这个变化肯定是一个突破性的变化。

【讨论】:

以上是关于为啥 ktor 序列化不支持序列化不同元素类型的集合?的主要内容,如果未能解决你的问题,请参考以下文章

Ktor自定义json对象反序列化

序列类型-列表的操作

Ktor:如何序列化/反序列化 JSON-API (vnd.api+json)

未找到“响应”类的 Ktor 序列化程序

数组与结构体的区别

Android Studio 未看到 Ktor 序列化程序(未找到“用户”类的序列化程序。)