Kotlin data classes and Gson

I’m in the middle of a project, maintaining, adding features, fixing bugs and so on. We are in a team of 8 developers and the ones of us working on the android project really loves the code style that kotlin brings us and so we decided to mix and match.

Just as a starter: It works great! We decided to switch presenters, modules and classes not coupled to android specific code first. So that we can get going directly since we’ve all heard that there can still be some issues doing just that.

And we got it up spinning in just matter of seconds. Now we got kotlin in production for around half a million users for several months mixed with java and they play nicely together.

Now I’m on a mission to convert even more code to Kotlin and at the same time clean up our networking functionality in the project. So my idea was to make kotlin data classes for models/value objects instead of the Pojo’s we got at the moment. And at the same moment switch my own built http-client to retrofit (if I can control the caching as I wish) with a gson converter. It all sounds fairly simple, but when I started to poke around with my unit tests for converting json to kotlin data classes I ran in to peculiar behavior that I didn’t expect..

For example, using only not nullable types.. just doesn’t make it. If a value is not in the json file. After conversion the value in the data class is null. Maybe thats what you could expect since such a converter can’t really guarantee that all values are represented in the json.

So as a solution to the issue I thought default-values! Thats the solution! But no.. The default values are just gone after gson has done it’s thing. So that didn’t help.

Okey, so my next approach was lateinit, and that can only be done with mutable variables (var), and I want immutable values.

Next up, delegates and lazy, that should do it! Sure I have to hide my vals automatically converted from json, but at least the classes consuming this data class will get a clean api and I’ll have null safety and nice defaults if the json didn’t contain the property.

But this also fails.. =(

This example should really not fail imho, it’s not the prettiest but it does what it’s supposed to:

data class Color( val name:String, private val hex:String? ) {
val argb:Int by lazy {
if( hex == null ) 0
else Integer.parseInt( hex, 16 )
}
}

I’m not sure exactly what happens but my guess is that Gson creates an instance with an empty constructor and reflects in the properties from the json objects. Cause when I create an instance of the class via the constructor everything works as expected.

My solution; remove the automagic perspective and keep the clean approach we wanted from the beginning, and add a deserializer to Gson for handling those types. Just extend the JsonDeserializer and override the deserialize function.

class ChannelDeserializer: JsonDeserializer<Color> {  override fun deserialize( 
json:JsonElement,
type:Type,
context:JsonDeserializationContext ):Color {
val root = json as JsonObject
val hex = root.getOrElse( "color", "000000" )
val name = root.getOrElse( "name", "[no name]" )
return Color( name, Integer.parseInt( hex, 16 ) )
}
}

Simple, clean and lovely to work with. The only downside is that you have to write the deserializers yourself.

PS. The getOrElse is an extension function I added to the JsonObject class to get nice defaults.

fun JsonObject.getOrElse(key:String, default:String ):String = 
get( key )?.asString ?: default

→ Bob

Lead Developer at Qvik, Coach, Agile Thinker, GDG Lead.

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