1

I have my code structure like this:

File 1:

abstract class SomeClass {
    abstract fun print()

    companion object {
        val versions = arrayOf(ClassV1::class, ClassV2::class)
    }
}

@Serializable
data class ClassV1(val x: Int) : SomeClass() {
    override fun print() {
        println("Hello")
    }
}

@Serializable
data class ClassV2(val y: String) : SomeClass() {
    override fun print() {
        println("World")
    }
}

File 2:

fun <T : SomeClass> getSomeObject(json: String, kClass: KClass<T>): SomeClass {
    return Json.decodeFromString(json)
}

fun printData(version: Int, json: String) {
    val someClass: SomeClass = getSomeObject(json, SomeClass.versions[version])
    someClass.print()
}

I have a json in printData that is a serialized form of some sub-class of SomeClass. I also have a version which is used to determine which class structure does the json represent. Based on the version, I want to de-serialize my json string to the appropriate sub-class of SomeClass.

Right now the getSomeObject function deserializes the json to SomeClass (which crashes, as expected). I want to know if there is a way I can deserialize it to the provided KClass.

I know I can do this like below:

val someClass = when (version) {
    0 -> Json.decodeFromString<ClassV1>(json)
    else -> Json.decodeFromString<ClassV2>(json)
}

But I am trying to avoid this since I can have a lot of such versions. Is there a better way possible?

Arpit Shukla
  • 9,612
  • 1
  • 14
  • 40
  • 1
    Does this help? https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md – dnault Nov 30 '21 at 21:06
  • @dnault I went through that doc. Couldn't understand the whole thing but looks like it's definitely more work than a `when` statement. – Arpit Shukla Dec 03 '21 at 04:45
  • @ArpitShukla You are trying to do polymorphic serialization. If you want to do that, you will need to understand/read through that document. It's essentially your answer. If you don't understand something about that documentation, maybe focus your question _on that_. P.s. no, it's not much more work than a `when` statement. `kotlinx.serialization`'s `SerializersModule` is essentially equivalent (logic-wise) to the `when` statement. – Steven Jeuris Dec 08 '21 at 14:24

1 Answers1

0

It seems to me that the following is what you are looking for:

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "version",
    visible = false)
@JsonSubTypes(
    JsonSubTypes.Type(value = ClassV1::class, name = "V1"),
    JsonSubTypes.Type(value = ClassV2::class, name = "V2"))
abstract class SomeClass {
    (...)
}  

This basically means that your JSON would be deserialized as ClassV1 or ClassV2 based on the JSON property version:

  • V1 would mean that ClassV1 is the target class;
  • V2 would mean that ClassV2 is the target class.

You can find more information about this at the following online resources:

João Dias
  • 16,277
  • 6
  • 33
  • 45
  • Thanks for the answer. I was wondering if I could do that using kotlinx.serialization as I can't afford multiple json parsing libraries in one project. But here also I will have to specify `JsonSubTypes.Type` for all `version`s. I guess I will go with my earlier `when` approach. – Arpit Shukla Dec 03 '21 at 04:41
  • With `kotlinx.serialization` I am not aware of any way to do this, but maybe it is possible. – João Dias Dec 03 '21 at 10:38
  • This is for _Jackson_. The OP is asking about `kotlinx.serialization`. -1 – Steven Jeuris Dec 08 '21 at 14:25