繁体   English   中英

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

[英]Polymorphic serialization of sealed hierarchies with generic type parameters

使用 Kotlin 序列化,我想使用密封层次结构中的类型参数对通用数据 class 进行序列化和反序列化(到 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

我不明白问题的根本原因 - 我是否需要显式注册其中一个插件生成的序列化程序? 还是我需要推出自己的序列化程序? 为什么会这样呢?

我正在使用 Kotlin 1.7.20 和 kotlinx.serialization 1.4.1

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

关于密封类状态的 KotlinX 序列化文档强调我的):

您必须确保序列化的 object 的编译时类型是多态的,而不是具体的

在文档的以下示例中,我们看到序列化子 class 而不是父 class 阻止它使用父(多态)类型反序列化。

在你的情况下,你有嵌套的多态类型,所以我认为这更复杂。 为了使序列化和反序列化工作,我尝试了多种方法,最后,我发现使它工作的唯一方法是:

  1. 删除 CodedData 上的泛型(以确保以多态方式解释code属性:
@Serializable
@SerialName("CodedData")
data class CodedData (
    override val number: Int,
    val info: String,
    val code: Coded
): NumberedData
  1. 编码时将编码数据 object 转换为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))

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM