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

问题描述

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

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

反序列化可以在以下代码中正常工作

@Serializable
data class Artist(
    val decomposed: JsonArray
)

fun main() {
    val jsonString = "{\"decomposed\":[\",\"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\":[\",\"genres\":[]}]}"
    println(Json.decodeFromString<Artist>(jsonString))
}

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

解决方法

您尝试做的事情称为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。