简体   繁体   中英

Deserialize JSON array with different values type with kotlinx.serialization library

I'm trying to deserialize following String:

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

Deserialization works fine with following code

@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))
}

But I want to do something like

@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))
}

But kotlinx.serialization expectedly fails with JsonDecodingException: Unexpected JSON token at offset 15: Expected '{, kind: CLASS' And I can't figure out how can I rewrite my Decomposed so deserialization work. Can you please help me out?

What you are trying to do is called polymorphic deserialization . It requires target classes of deserialization to have a common superclass (preferrably sealed):

@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

This will allow you to deserialize JSON of the following format:

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

Since there is no class descriminator in original JSON, serialization library can't determine the actual serializer which should be used to deserialize Kotlin class. You will have to write custom JsonContentPolymorphicSerializer and wire it to Decomposed class; also you have to write custom serializer for StringValue class, as it is represented in JSON as a String , not a JSONObject with a value field of String type:

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()

This will allow you to deserialize JSON of original format.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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