Why I ditched beloved Gson for my Kotlin project

Abhishek Bansal
May 16, 2020 · 5 min read

I have been developing Android apps for about 6.5 years now. For every small or big Android app I have churned out, I have blindly used Gson for serialization and deserialization. Gson seemed to be flawless as it almost read my mind.

This changed in one of my recent Kotlin only project. Like every other project we started off with Gson here as well. But then, things changed when we started facing dreaded NullPointerExceptions. But wait, did I not say Kotlin? And isn't Kotlin supposed to be our solution for this billion dollar problem?

Well no! Kotlin can only do so much if developers are so determined to get NullPointerExceptions. Gson uses reflection for deserializing JSON to Java or Kotlin objects. Java does not have notion of nullable types like Kotlin. Gson was originally written with Java in mind and it fails to understand difference between nullable and non-nullable types of Kotlin.

In this project our APIs were not mature enough to include all possible data validations and schema was not as strictly defined. So app will crash here and there while testing against dev servers, because some field that we have declared as non-nullable did not come in API or it did not come in right format. This happened because some other client is still developing feature or due to some bug. There was a possibility that something like this may happen in production as well.

In order to avoid this we were left with two choices,

  1. We make all our model fields nullable. i.e. our model will look something like this
data class Person(
val id: String?,
val name: String?,
val gender: Gender?,
val address: Address?
)

Which feels wrong on so many levels.

  • This will make bunch of code wrapped in .let{} calls unnecessarily
  • In my opinion it just looks ugly and makes code hard to follow because existence of every field is uncertain.
  • In most of the cases it does not even make sense to deserialize an object which is corrupted and unusable in app anyways. e.g. let’s suppose there is a feature where app allows user to edit details and post it on server with help of id. In this case an object with null id will break the feature. Not to mention you will have to do special handling for this case. Its best to have id as non-nullable field.

2. Use some evil getter setter hack like illustrated in this article

Both of these are unacceptable and sort of break Kotlin's soul. It is when I decided to move project to Moshi. Moshi is small when compared to Gson and tries to stay focused by doing less, but, its power lies in its simplicity. Moshi understands difference between nullable and non-nullable fields in Kotlin. This single handedly became deciding factor for me.

Knowing that if a given field is declared as non-null it will not be null under any circumstance, suddenly increased the trust factor in my own code by manifolds.

Let’s look at some other benefits of using Moshi over Gson.

Moshi doesn't silently fail when there is a mismatch in field data type or nullbility contract. It raises a JSONDataException which clients are responsible for handling. This makes you aware about subtle mistakes which are easily missed with Gson.

Gson is purely based on reflection where as Moshi supports both code generation and reflection. Reflection support can be added with moshi-reflect artefact but I don't really use it (Reflection is Slow and has same problems as Gson!). Code generation is supported in version 1.6+ with moshi-kotlin-codegen annotation processor.

As per this benchmarking Moshi is faster then Gson in serialization and de-serialization.

Let’s consider this JSON

{
"vehicles": [
{
"model": "m1",
"type": "car",
"tyres": [
{ "type": "mrf" },
{ "type": "apollo" }
]
},
{
"type": "truck",
"tyres": [
{ "type": "mrf" },
{ "type": "apollo" },
{ "type": "apollo" }
]
}
]
}

Here corresponding Kotlin data classes

data class VehicleContainer(val vehicles: List)

abstract class Vehicle {
abstract val model: String
abstract val type : String
abstract val tyres: List?
}

data class Car(override val model: String,
override val type: String,
override val tyres: List?): Vehicle()

data class Truck(override val model: String,
override val type: String,
override val tyres: List?): Vehicle()

data class Tyre(val type: String)

Things to note:

  1. We are working with polymorphic list of objects here. Vehicle is base class, Car and Truck are derived classes.
  2. model and type variables are mandatory and cannot be null.

Let’s see how Gson and Moshi can deserialize this.

In order to deserialize List<Vehicle> we need to add support for RuntimeTypeAdapterFactory.

Here is how Gson object will be constructed

val vehicleAdapterFactory: RuntimeTypeAdapterFactory =
RuntimeTypeAdapterFactory.of(Vehicle::class.java, "type")
.registerSubtype(Vehicle::class.java, "car")
.registerSubtype(Vehicle::class.java, "truck")

val Gson = GsonBuilder().registerTypeAdapterFactory(vehicleAdapterFactory)
.create()

Now to deserialize

val vehicleContainer = Gson.fromJson(jsonStr, VehicleContainer.class)

Note that even though our original JSON does not include model for Truck object Gson will deserialize this object and set model to null which may lead to unexpected NullPointerException later.

Moshi also supports polymorphic serialization and deserialization via its PolymorphicJsonAdapterFactory

Before deserialization moshi need to be able to generate relevant adapters. This is done via @JsonClass(generateAdapter = true) annotation. For example

@JsonClass(generateAdapter = true)
data class Car(override val model: String,
override val type: String,
override val tyres: List?): Vehicle()

Next is setting up Moshi instance

val vehicleAdapterFactory = PolymorphicJsonAdapterFactory.of(Vehicle::class.java, "type")
.withSubtype(Car::class.java, "car")
.withSubtype(Truck::class.java, "truck")

val moshi = Moshi.Builder().add(vehicleAdapterFactory).build()

Now deserialize

try {
val vehicleContainer = moshi.adapter(VehicleContainer::class.java)
.fromJson(jsonStr, VehicleContainer.class)
} catch(error: JsonDataException) {
}

Moshi finds that second object in list does not respect language contract and hence, it fails the deserialization process. You will get a JSONDataException instead of expected VehicleContainer object.

This is a little nuance because in my case I still want to have objects which respect full contract like Car object in this example. We are using a modified version of this SkipBadElementsListAdapter which solves the problem for us.

Moshi object now builds as follows

val moshi = Moshi.Builder()
.add(vehicleAdapterFactory)
.add(SkipBadElementsListAdapter.Factory)
.build()

After this deserialization process we get a nice vehicles list inside VehiclesContainer object with one pristine object.

Similarly you can use other adapters available here and here to customize moshi as per your needs or you can write your own.

In my experience while Moshi is some work to begin with first time. It really pays off in long term because of increased trust and quality in your own code. After a migration period of about 2 weeks we never had to deal with those unexpected NullPointerException's because of somebody else's mistake.

  1. Jake Wharton’s Talk- A Few Ok Libraries
  2. Moshi BenchMarks
  3. Gson Design Document
  4. Reddit Thread

The Startup

Get smarter at building your thing. Join The Startup’s +787K followers.

Sign up for Top 10 Stories

By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +787K followers.

Abhishek Bansal

Written by

Android and all things Tech

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +787K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store