tl;dr:跳到底部的完整示例
问题总结
你有一个多态类,并且类型由属性决定outside班级的。
{
"type": 2, <- extract this
"data": { <- determined by 'type'
"id": "0001",
"name": "MEGATRON"
}
}
Kotlinx 序列化提供了处理此问题的工具 - 但它们需要一些组装。
基于 JSON 内容的多态反序列化
由于您正在使用 JSON,因此可以使用基于内容的多态反序列化 https://github.com/Kotlin/kotlinx.serialization/blob/v1.4.1/docs/json.md#content-based-polymorphic-deserialization.
这是一个初步的实现,但有一个缺陷......
object InteractionJsonSerializer : JsonContentPolymorphicSerializer<Interaction>(
Interaction::class
) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out Interaction> {
// extract the type from the plain JSON object
val type = element.jsonObject["type"]?.jsonPrimitive?.intOrNull
println("found InteractionData type: $type")
return when (type) {
// can't specify the type of InteractionData
2 -> Interaction.serializer()
3 -> Interaction.serializer()
else -> error("unknown type $type")
}
}
}
不可能选择特定的序列化器,因为Interaction
没有类型参数,所以让我们添加一个。
@Serializable
data class Interaction<T : InteractionData?>( // add a type parameter
val type: Byte,
val data: T? = null
)
现在 Kotlinx 序列化插件将生成一个序列化器 https://github.com/Kotlin/kotlinx.serialization/blob/v1.4.1/docs/serializers.md#plugin-generated-generic-serializer接受序列化器T: InteractionData
。我们可以更新InteractionJsonSerializer
来利用这个。
object InteractionJsonSerializer : JsonContentPolymorphicSerializer<Interaction<*>>(
Interaction::class
) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out Interaction<*>> {
val type = element.jsonObject["type"]?.jsonPrimitive?.intOrNull
println("found InteractionData type: $type")
return when (type) {
// now the type can be specified
2 -> Interaction.serializer(ApplicationCommandData.serializer())
3 -> Interaction.serializer(MessageComponentData.serializer())
else -> error("unknown type $type")
}
}
}
完整示例
这是一个完整的、可运行的示例,包含所有导入。
我对你的代码做了一些调整。
- I made
InteractionData
a 密封接口 https://kotlinlang.org/docs/sealed-classes.html,因为它看起来合适
- 我将课程转换为数据类 https://kotlinlang.org/docs/data-classes.html,所以 Kotlin 生成了一个很好的
toString()
.
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.intOrNull
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
fun main() {
val interactionType2 =
Json.decodeFromString(
InteractionJsonSerializer,
/*language=JSON*/
"""
{
"type": 2,
"data": {
"id": "0001",
"name": "MEGATRON"
}
}
""".trimIndent()
)
println(interactionType2)
val interactionType3 =
Json.decodeFromString(
InteractionJsonSerializer,
/*language=JSON*/
"""
{
"type": 3,
"data": {
"custom_id": "abc123"
}
}
""".trimIndent()
)
println(interactionType3)
}
@Serializable
data class Interaction<T : InteractionData?>(
val type: Byte,
val data: T? = null
)
sealed interface InteractionData
@Serializable
data class ApplicationCommandData(
val id: String,
val name: String
) : InteractionData
@Serializable
data class MessageComponentData(
val custom_id: String
) : InteractionData
object InteractionJsonSerializer : JsonContentPolymorphicSerializer<Interaction<*>>(
Interaction::class
) {
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<out Interaction<*>> {
val type = element.jsonObject["type"]?.jsonPrimitive?.intOrNull
println("found InteractionData type: $type")
return when (type) {
2 -> Interaction.serializer(ApplicationCommandData.serializer())
3 -> Interaction.serializer(MessageComponentData.serializer())
else -> error("unknown type $type")
}
}
}
Output
found InteractionData type: 2
Interaction(type=2, data=ApplicationCommandData(id=0001, name=MEGATRON))
found InteractionData type: 3
Interaction(type=3, data=MessageComponentData(custom_id=abc123))
Versions
- 科特林 1.7.21
- Kotlinx 序列化 1.4.1