Hopp til hovedinnhold

Teknologi / 8 minutter /

JSON Content Polymorphic Serialization with Kotlinx

Did you ever receive JSON containing a list of objects that share some properties, but certain selected properties were distinct? Maybe you thought that these "subtypes" could probably de-serialize into subclasses, but you lack the 'type' property to distinguish on. Don't fret. With Kotlinx.serialization, you can still achieve polymorphism.

Polymorph (c) 1993-2009 Robert Bliss / Wizards of the Coast

A brief introduction to polymorphism

Polymorphism is the provision of a single interface to entities of various types, which means applying multiple meanings or behaviours (implementations) to a single object type depending on the context. In real life, one may think of a man implementing various behaviours as a father, a husband, a co-worker etc. depending on the situation.

  • When I first learned object-oriented programming, the classical image of polymorphism portrayed the interface of a drawable object (a shape), where the various implementations would render e.g. a rectangle, triangle, circle etc.

The strengths of polymorphism come to play when whoever is using the object doesn't really care about the final shape or rendering of said object - We just want the damn drawing to render its final shape. To do this, we call the interface's draw() function, rather than asking the object 'What shape are you?', before trying to call the appropriate drawTriangle(), drawRectangle(), drawCircle(), or any of the other implementations.

Kotlin and Kotlinx

Kotlin is a modern, statically typed, cross-platform programming language with type inference. Some refer to it as the "Java alternative" (it is completely Java interoperable). With its growing popularity as a functional and pragmatic programming language, many programmers are looking to explore its potential.

Kotlinx is an extension library for Kotlin, and includes (among other things) a set of wrappers and helpers for various pre-existing Java libraries.

Kotlinx.coroutines has become a well-known extension allowing support for suspendable computation.

Kotlinx.serialization provides extensions for serializing and de-serializing data formats such as JSON and Google protocol buffers.

Polymorphism and JSON with Kotlinx

Traditionally, JSON polymorphic serialization and de-serialization requires the presence of a 'type' property. Various implementation of object mappers have provided different approaches to handling polymorphic JSON, and most of them rely on this property. Jackson Annotations, for instance, will allow use of the '@JsonTypeInfo' to specify type information used for serialization and de-serialization.

However, you may find yourself in a situation where you receive a JSON which lacks this 'type' property, and you need to interpret the content of the object before you de-serialize it.

1[
2  {
3    "id":1,
4    "amount":"1.0",
5    "date":"02.03.2022"
6    
7  },
8  {
9    "id":2,
10    "amount":"1.0",
11    "date":"02.03.2022",
12    "cardName":"Scrooge McDuck"
13  }
14]

Kotlinx allows for custom serialization without the presence of a dedicated discriminator, such as 'type', by use of the base class JsonContentPolymorphicSerializer

1object PaymentDetailsSerializer : JsonContentPolymorphicSerializer<PaymentDetails>(PaymentDetails::class) { 
2  override fun selectDeserializer(element: JsonElement) = when {
3    "cardName" in element.jsonObject -> CreditCardPaymentDetails.serializer()
4    else -> StandardPaymentDetails.serializer()
5  }
6}

Here we will choose a serializer based on the presence of the 'cardName' property.

1Json.decodeFromString(
2  PaymentDetailsSerializer,
3  """{"id":1,"amount":"1.0","date":"02.03.2022"}"""
4  )
5Json.decodeFromString(
6  PaymentDetailsSerializer,
7  """{"id":2,"amount":"1.0","date":"02.03.2022","cardName":"Scrooge McDuck"}"""
8  )

Simples! ;)

Caveats

Since JSON content is represented by the JsonElement class, and could only be read with JsonDecoder, this class only works with Json format.

You may find working examples, in form of a simple web application and unit tests on git.