使用密封类进行序列化在 Kotlin 序列化中失败

huangapple go评论62阅读模式
英文:

Serialization with sealed classes fails in Kotlin serialization

问题

在以下用例中,我在使用Kotlin序列化时遇到了问题:

@Serializable
sealed class NetworkAnswer {
    @SerialName("answerId")
    abstract val id: Int
}

@Serializable
data class NetworkYesNoAnswer(
    override val id: Int,

    @SerialName("isPositive")
    val isPositive: Boolean
) : NetworkAnswer()

当我序列化时:

val json = Json { ignoreUnknownKeys = true; explicitNulls = false }
val result: NetworkYesNoAnswer = json.decodeFromString(NetworkYesNoAnswer.serializer(), """
            { 
            "answerId": 1, 
            "isPositive": true 
            }
  """.trimIndent()
)

我遇到了以下错误:

Caused by: kotlinx.serialization.MissingFieldException: Fields [id] are required for type with serial name 'NetworkYesNoAnswer', but they were missing

唯一让序列化正常工作的方法是,如果我同时使用成员名和"SerialName"相同的名称,如下所示:

@Serializable
sealed class NetworkAnswer {
    @SerialName("answerId")
    abstract val answerId: Int
}

@Serializable
data class NetworkYesNoAnswer(
    override val answerId: Int,

    @SerialName("isPositive")
    val isPositive: Boolean
) : NetworkAnswer()

这有点违背了"SerialName"的目的,有没有一种方法可以解决这个问题,而不必使用相同的名称?

英文:

I'm having trouble with kotlin-serialization in the following use case:

@Serializable
sealed class NetworkAnswer {
    @SerialName("answerId")
    abstract val id: Int
}

@Serializable
data class NetworkYesNoAnswer(
    override val id: Int,

    @SerialName("isPositive")
    val isPositive: Boolean
) : NetworkAnswer()

When I serialize this:

val json = Json { ignoreUnknownKeys = true; explicitNulls = false }
val result: NetworkYesNoAnswer = json.decodeFromString(NetworkYesNoAnswer.serializer(), """
            { 
            "answerId": 1, 
            "isPositive": true 
            }
  """.trimIndent()
)

I get the following error

Caused by: kotlinx.serialization.MissingFieldException: Fields [id] are required for type with serial name 'NetworkYesNoAnswer', but they were missing

The only way the serialization works is if I use the same name for both the member and "SerialName", like so:

@Serializable
sealed class NetworkAnswer {
    @SerialName("answerId")
    abstract val answerId: Int
}

@Serializable
data class NetworkYesNoAnswer(
    override val answerId: Int,

    @SerialName("isPositive")
    val isPositive: Boolean
) : NetworkAnswer()

This kinda defeats the purpose of "SerialName", is there a way to solve that without using the same name?

答案1

得分: 1

在基类上声明@SerialName对于子类覆盖的成员声明没有影响。

相反,您可以在子类上声明@SerialName。无需更改字段的实际名称。

@Serializable
data class NetworkYesNoAnswer(
    @SerialName("answerId")
    override val id: Int,

    @SerialName("isPositive")
    val isPositive: Boolean
) : NetworkAnswer()

目前似乎不支持在基类上声明@SerialName并将其应用于所有子类,但社区中的其他成员也希望支持此功能,例如GitHub上的这里


OT:很可能您可以使用sealed interface,它首次在Kotlin v1.5.0中引入,而不是使用sealed class

英文:

Declaring a @SerialName on a base class has no effect on member declarations overridden by child classes.

Instead, you can declare @SerialName on the child class instead. There is no need to change the actual name of the field.

@Serializable
data class NetworkYesNoAnswer(
    @SerialName("answerId")
    override val id: Int,

    @SerialName("isPositive")
    val isPositive: Boolean
) : NetworkAnswer()

Declaring the @SerialName on the base class and applying it to all children seems NOT to be supported as of now, but is desired by other members of the community as well, e.g. here on GitHub.


OT: Most likely you could use a sealed interface, which was first introduced in Kotlin v1.5.0, instead of a sealed class.

huangapple
  • 本文由 发表于 2023年2月14日 19:55:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/75447482.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定