使用 kotlinx.serialization 库反序列化具有不同值类型的 JSON 数组

Posted

技术标签:

【中文标题】使用 kotlinx.serialization 库反序列化具有不同值类型的 JSON 数组【英文标题】:Deserialize JSON array with different values type with kotlinx.serialization library 【发布时间】:2021-02-08 05:34:31 【问题描述】:

我正在尝试反序列化以下字符串:

 val stringJson = "\"decomposed\":[\", \",\"id\":4944372,\"name\":\"Johny\",\"various\":false,\"composer\":false,\"genres\":[]]"
   

反序列化可以很好地使用以下代码

@Serializable
data class Artist(
    val decomposed: JsonArray
)

fun main() 
    val jsonString = "\"decomposed\":[\", \",\"id\":4944372,\"name\":\"Johny\",\"various\":false,\"composer\":false,\"genres\":[]]"
    println(Json.decodeFromString<Artist>(jsonString))

但我想做类似的事情

@Serializable
class Decomposed 
    @Serializable
    class DecomposedClassValue(val value: DecomposedClass)

    @Serializable
    class StringValue(val value: String)



@Serializable
data class DecomposedClass(
    val id: Long? = null,
    val name: String? = null,
    val various: Boolean? = null,
    val composer: Boolean? = null,
    val genres: JsonArray? = null
)

@Serializable
data class Artist(
    val decomposed: List<Decomposed>
)

fun main() 
    val jsonString = "\"decomposed\":[\", \",\"id\":4944372,\"name\":\"Johny\",\"various\":false,\"composer\":false,\"genres\":[]]"
    println(Json.decodeFromString<Artist>(jsonString))

但是kotlinx.serialization 预计会失败,JsonDecodingException: Unexpected JSON token at offset 15: Expected ', kind: CLASS' 而且我不知道如何重写我的Decomposed 以便反序列化工作。你能帮帮我吗?

【问题讨论】:

是否可以调整 JSON 的格式或者这是一个硬性要求? 【参考方案1】:

您尝试执行的操作称为polymorphic deserialization。 它要求反序列化的目标类有一个共同的超类(最好是密封的):

@Serializable
data class Artist(
    val decomposed: List<Decomposed>
)

@Serializable
sealed class Decomposed

@Serializable
class StringValue(val value: String) : Decomposed() //Can't add superclass to String, so we have to create a wrapper class which we could make extend Decomposed

@Serializable
data class DecomposedClass(
    val id: Long? = null,
    val name: String? = null,
    val various: Boolean? = null,
    val composer: Boolean? = null,
    val genres: JsonArray? = null
) : Decomposed() //DecomposedClassValue is redundant, we may extend DecomposedClass from Decomposed directly

这将允许您反序列化以下格式的 JSON:

val jsonString = "\"decomposed\":[\"type\":\"StringValue\", \"value\":\",\", \"type\":\"DecomposedClass\", \"id\":4944372,\"name\":\"Johny\",\"various\":false,\"composer\":false,\"genres\":[]]" 

由于原始 JSON 中没有 class descriminator,序列化库无法确定应该用于反序列化 Kotlin 类的实际序列化程序。您必须编写自定义 JsonContentPolymorphicSerializer 并将其连接到 Decomposed 类;您还必须为StringValue 类编写自定义序列化程序,因为它在JSON 中表示为String,而不是具有value 字段String 类型的JSONObject:

object DecomposedSerializer : JsonContentPolymorphicSerializer<Decomposed>(Decomposed::class) 
    override fun selectDeserializer(element: JsonElement) = when 
        element is JsonPrimitive -> StringValue.serializer()
        else -> DecomposedClass.serializer()
    


object StringValueSerializer : KSerializer<StringValue> 
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("StringValue")

    override fun deserialize(decoder: Decoder): StringValue 
        require(decoder is JsonDecoder)
        val element = decoder.decodeJsonElement()
        return StringValue(element.jsonPrimitive.content)
    

    override fun serialize(encoder: Encoder, value: StringValue) 
        encoder.encodeString(value.value)
    


@Serializable(with = DecomposedSerializer::class)
sealed class Decomposed

@Serializable(with = StringValueSerializer::class)
class StringValue(val value: String) : Decomposed()

这将允许您反序列化原始格式的 JSON。

【讨论】:

以上是关于使用 kotlinx.serialization 库反序列化具有不同值类型的 JSON 数组的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 kotlinx.serialization 部分解码 JSON 字符串?

Ktor 与 Kmongo 和 kotlinx.serialization

使用 kotlinx.serialization 在 ktor 服务器中接收 Map

在 Kotlin/Native 中使用 kotlinx.serialization 进行多态反序列化

如何将 Spring Boot 映射器从 Jackson 交换到 kotlinx.serialization

使用 Kotlinx.serialization 将 JSON 数组解析为 Map<String, String>