kotlin 具有泛型类型参数的密封层次结构的多态序列化

xriantvc  于 2023-01-31  发布在  Kotlin
关注(0)|答案(1)|浏览(120)

使用Kotlin序列化,我想序列化和反序列化(到JSON)一个来自密封层次结构的带有类型参数的泛型数据类,但是,我得到了一个运行时异常。
要重现此问题:

import kotlinx.serialization.*
import kotlin.test.Test
import kotlin.test.assertEquals

/// The sealed hierarchy used a generic type parameters:
@Serializable
sealed interface Coded {
    val description: String
}

@Serializable
@SerialName("CodeOA")
object CodeOA: Coded {
    override val description: String = "Code Object OA"
}

@Serializable
@SerialName("CodeOB")
object CodeOB: Coded {
    override val description: String = "Code Object OB"
}

/// Simplified class hierarchy
@Serializable
sealed interface NumberedData {
    val number: Int
}

@Serializable
@SerialName("CodedData")
data class CodedData<out C : Coded> (
    override val number: Int,
    val info: String,
    val code: C
): NumberedData

internal class GenericSerializerTest {
    @Test
    fun `polymorphically serialize and deserialize a CodedData instance`() {
        val codedData: NumberedData = CodedData(
            number = 42,
            info = "Some test",
            code = CodeOB
        )
        val codedDataJson = Json.encodeToString(codedData)
        val codedDataDeserialized = Json.decodeFromString<NumberedData>(codedDataJson)
        assertEquals(codedData, codedDataDeserialized)
    }
}

在以下运行时异常中运行测试结果:

kotlinx.serialization.SerializationException: Class 'CodeOB' is not registered for polymorphic serialization in the scope of 'Coded'.
Mark the base class as 'sealed' or register the serializer explicitly.

这个错误消息对我来说没有意义,因为两个层次结构都被密封并标记为@Serializable
我不明白问题的根本原因-我需要显式地注册一个插件生成的序列化器吗?或者我需要滚动我自己的序列化器吗?为什么会这样?
我使用的是Kotlin1.7.20和kotlinx。序列化1.4.1

avwztpqn

avwztpqn1#

  • 免责声明 *:我不认为我的解决办法是非常令人满意的,但我现在找不到更好的办法。

有关密封类状态的KotlinX序列化文档(* emphasis mine *):
你必须确保序列化对象的编译时类型是多态的,而不是具体的
在下面的文档示例中,我们看到序列化子类而不是父类阻止了使用父(多态)类型对其进行反序列化。
在你的例子中,你有嵌套的多态类型,所以我认为这更加复杂。为了使序列化和反序列化工作,我尝试了很多方法,最后,我发现唯一的方法是:
1.删除CodedData上的泛型(以确保以多态方式解释code属性:

@Serializable
@SerialName("CodedData")
data class CodedData (
    override val number: Int,
    val info: String,
    val code: Coded
): NumberedData

1.编码时将编码数据对象强制转换为NumberedData,以确保触发多态性:

Json.encodeToString<NumberedData>(codedData)

使用基于您自己的单元测试的小主程序进行测试:

fun main() {
    val codedData = CodedData(
        number = 42,
        info = "Some test",
        code = CodeOB
    )
    val json = Json.encodeToString<NumberedData>(codedData)
    println(
        """
            ENCODED:
            --------
            $json
        """.trimIndent()
    )

    val decoded = Json.decodeFromString<NumberedData>(json)
    println(
        """
            DECODED:
            --------
            $decoded
        """.trimIndent()
    )
}

它打印:

ENCODED:
--------
{"type":"CodedData","number":42,"info":"Some test","code":{"type":"CodeOB"}}
DECODED:
--------
CodedData(number=42, info=Some test, code=CodeOB(description = Code Object OB))

相关问题