Kotlinx 序列化:如何规避反序列化的具体化类型参数?

Rap*_*ita 5 generics json kotlin kotlin-reified-type-parameters kotlinx.serialization

实际上,主要问题仍然是 Kotlin 中的类没有具体化的类型参数。但这就是为什么在这个特定情况下这让我感到困扰:

假设您有一个包装类Wrapper,它接受一个字符串content和一个类* ,并且可以通过调用函数按需解析为 JSON 来输出检索到的type类的对象:typecontentgetObj()

class Wrapper<T>(private val content: String, /*private val type: KClass<*>*/) {
    fun getObj(): T {
        // ?
    }
}
Run Code Online (Sandbox Code Playgroud)

我想使用 kotlinx.serialization。现在,你可能已经注意到我之前是如何在“class”后面加星号的。原因是:是的,Wrapper必须以某种方式参加目标课程,但是如何呢?它应该只是 typearg (不起作用,因为类型erausre)还是引用KClass(不起作用,因为我需要具体化的 typearg)?

事实是,据我所知,将通用 JSON 解码为可序列化目标类的唯一方法是使用Json.decodeFromString<T>(content),其中T是目标类型,content是 JSON 字符串。现在,T被定义为具体化(以便可以在运行时处理类型)并且只能用另一个具体化的 typearg 或实际的类引用来填充。我无法使用另一个具体化的类型参数,因为我处于类的上下文中,并且类不能具有具体化的类型参数。我也不能使用实际的类引用,因为类的用户应该能够使用不同的目标来构造它,例如他们决定目标是什么,而不是我。

那么,我该如何使用 kotlinx.serialization 来做到这一点?有可能吗?

Rap*_*ita 7

好吧,还没人回答这个问题,但我也在 r/Kotlin subreddit 中发布了这个问题。这里是

我实际上在那里得到了答案(归功于 u/JakeWharton),并且由于您可能会遇到这个 StackOverflow 问题,因为您在 google 上搜索了相同的问题,因此您可能会很高兴在这里找到答案。所以这是我尝试解释的答案:

所以,基本上,kotlinx-serialization 确实不适用于KClasses。但当你考虑一下时,你只需要KClass确定如何序列化它。由于这是在使用 KXS 时在编译时确定的,因此您实际上只需要传递序列化器(定义如何序列化/反序列化类的实际策略)。您可以@Serializable通过调用.serializer()它来为每个带有注释的类获取序列化器;结果将是类型KSerializer<T>. 所以,而不是有

class Wrapper<T>(private val content: String, private val type: KClass<T>)
Run Code Online (Sandbox Code Playgroud)

并通过构建它

val wrapper = Wrapper("{}", Foo::class)
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

class Wrapper<T>(private val content: String, private val serializer: KSerializer<T>)
Run Code Online (Sandbox Code Playgroud)

然后像这样构造它:

val wrapper = Wrapper("{}", Foo.serializer())
Run Code Online (Sandbox Code Playgroud)

(假设Foo用 注释@Serializable

然后,您可以使用KSerializer代替 typearg 来序列化和反序列化,如下所示:

val obj: T = Json.decodeFromString(serializer, "[Your JSON String]")
val str: String = Json.encodeToString(serializer, obj)
Run Code Online (Sandbox Code Playgroud)

就是这样!只需更换您的常规(K)Class方法,KSerializer它就可以与 KXS 一起使用。