What is the easiest way to migrate concrete serial...
# serialization
a
What is the easiest way to migrate concrete serializers to polymorphic ones, while still supporting previously serialized old objects? My old impl:
Copy code
@Serializable
data class MyConcreteDataClass(val name: String)

@Serializable
data class MyCommonClass(val value: MyConcreteDataClass)
I want to support a new implementation while also maintaining compatibility:
Copy code
@Serializable
sealed interface AllDataClasses {
    val name: String
}

@Serializable
data class MyConcreteDataClass(override val name: String): AllDataClasses

@Serializable
data class MyNewConcreteDataClass(override val name: String): AllDataClasses

@Serializable
data class MyCommonClass(val value: AllDataClasses)
May be there is a way to provide a fallback serializer if legacy object doesn’t point to polymorphic object?
e
Maybe off topic - Is your API versioned? Why would you want to support the old way?
a
I have a serialized json files that stored on users desktops
e
Clear! You can always write a custom serializer with
try/catch
and in catch try to deserialise it in the old way. On Android we usually always migrate data on the update if needed. Like if database schema was changed, etc. Maybe you should try to do that?
BTW, you can also add versioning to local files
a
It is not an Android app. Try catch a bit tricky, because now app uses streaming json decoder that can’t revert its state after token was consumed
b
I bumped into this issue many times before as well, I think it's time to write a feature request for this
I ended up creating a custom deserializer using
JsonContentPolymorphicSerializer
and checking for the missing discriminator, with he help of
@KeepGeneratedSerialiozer
just did some more testing here and
polymorphicDefaultDeserializer
seems to be a better solution for this. the following code works for me:
Copy code
fun main() {
    val json = Json {
        serializersModule = SerializersModule {
            polymorphicDefaultDeserializer(ChildWithPoly::class) { ChildWithPoly.ChildLegacy.serializer() }
        }
    }
    val obj = Parent(ChildWithPoly.ChildLegacy("Hello World!"))

    assert(obj == json.decodeFromString<Parent>("""{"child":{"type":"ChildLegacy", "s":"Hello World!"}}"""))
    assert(obj == json.decodeFromString<Parent>("""{"child":{"s":"Hello World!"}}"""))
}

@Serializable
data class Parent(
    val child: ChildWithPoly,
)

@Serializable
sealed interface ChildWithPoly {

    @Serializable
    @SerialName("ChildLegacy")
    data class ChildLegacy(
        val s: String,
    ): ChildWithPoly

    @Serializable
    @SerialName("ChildNew")
    data class ChildNew(
        val s: String,
    ): ChildWithPoly
}

// previously not part of a hierarchy
@Serializable
data class ChildNoPoly(
    val s: String,
)
a
@Bruno Medeiros Thanks for the answer. Yes, I’ve tried to provide the
polymorphicDefaultDeserializer
, but it won’t work in my case. Your example works. I started investigating the problem and found that
polymorphicDefaultDeserializer
does not work with
Json { useArrayPolymorphism = true }
. I will create an issue.
b
do you use array polymorphism? I always wondered in which case that would be useful hehe
I think it's going to be hard to implement it there because the underlying types are completely different. maybe the first approach I mentioned will work better for you
a
less symbols :)
Copy code
{"child":["ChildLegacy",{"s":"Hello World!"}]} // with array polymorphism
{"child":{"type":"ChildLegacy","s":"Hello World!"}} // without
We can't just switch to
useArrayPolymorphism = false
because we already have a lot of files in that (arrayPolymorphism) format
b
you save one word, man haha. sorry, off topic. and yes, too late to change that anyway
I think it's fair if you raise an issue, but I had something on the back of my mind saying this array mode is some legacy thing to support some compatibility
a
one word in 1 thousand places it is a 7*1000 symbols
and yes, we have a really big json files
a
Annotation is not allowed on classes involved in polymorphic serialization: interfaces, sealed classes, abstract classes, classes marked by Polymorphic.
b
if it's an array, you delegate to the generated serializer, otherwise you delegate to to ChildLegacy serializer
oh, the keep one is not allowed?
I remember I made this work in some way, I'll try to find the code here