Goodbye RuntimeTypeAdapterFactory. Polymorphic serialization using kotlinx.serialization

Andre Classen
2 min readApr 28, 2019
Photo by: Sarah Pflug

In an effort to use as many standard Kotlin tools as possible in our Android apps I made an effort to replace Gson with Kotlin Serialization and eventually fell in ❤ with it. Most of this was straightforward, however one thing got me thinking.

Let’s assume we have a JSON array where the type is defined by a property value:

[
{
"type": "Rect",
"width": 100,
"height": 50
},
{
"type": "Circle",
"radius": 154
}
]

With Gson deserializing this is pretty easy using a RuntimeTypeAdapterFactory.

Luckily the latest improvements to the @Polymorphic annotation in Kotlin Serialization 0.11.0 make this task a piece of cake as well.

To proper deserialize the above example structure, let’s define the data classes like:

@Polymorphic
@Serializable
abstract class BaseGeometric
@SerialName("Rect")
@Serializable
data class Rectangle(val width: Int, val height: Int) : BaseGeometric()
@SerialName("Circle")
@Serializable
data class Circle(val radius: Double) : BaseGeometric()

Note the @SerialName at class definition! By default the full package name is used for de-/serialization, but by using @SerialName we can define our own name.

Now we just need to register our BaseGeometric subclasses:

val geometricsModule = SerializersModule {
polymorphic(BaseGeometric::class) {
subclass(Rectangle::class)
subclass(Circle::class)
}
}

Sample usage:

val src = listOf(Rectangle(100, 50), Circle(154.0))
Json {
serializersModule = geometricsModule
}
.apply {
val
json = encodeToString(src)
assert(json == "[{\"type\":\"Rect\",\"width\":100,\"height\":50},{\"type\":\"Circle\",\"radius\":154.0}]")
val polyList = decodeFromString<List<BaseGeometric>>(json)
assert(polyList == src)
}

Here we are leveraging the fact that "type" is the default Class Discriminator in Kotlin Serialization. However this can be easily changed via JsonConfiguration to whatever is needed. Here we are using "geo_type" instead of "type":

Json {
serializersModule = geometricsModule
classDiscriminator = "geo_type"
}.apply {
val
json = encodeToString(src)
assert(json == "[{\"geo_type\":\"Rect\",\"width\":100,\"height\":50},{\"geo_type\":\"Circle\",\"radius\":154.0}]")
val polyList = decodeFromString<List<BaseGeometric>>(json)
assert(polyList == src)
}

Happy serializing!

--

--