Custom Interface Adapter To Serialize And Deserialize Interfaces in Kotlin using Gson

Paul Stanescu
3 min readNov 8, 2018

--

Sometimes you want to do custom things, but in the same time you don’t want to set clear boundaries. That means that, mainly, you have mostly the same logic behind, but in the same time something can be different. Yes, as you already anticipate, generics are the answer.

mostly the same but different in the end

What is Gson?

Gson is a library that can be used to convert Objects into their JSON representation

Let’s take an example to understand better how this idea works

In my case I have a menu with different options. Translated into a natural language: “each menu option has a different content” but in the end it is still a menu option. So, I have to create a list of options, each entry will be defined by a type and its content. The content will be a generic one to be able to support any format.

Time to code

class MenuListModel<T : MenuContent> {

var type: MenuListType? = null

var content: T? = null
}

Note: MenuListType is an enumeration of possible menu types

enum class MenuListType {

TYPE1,
TYPE2,
etc
}

In order to have a unique solution everywhere, we should have a shared definition like a contract. The contract means interface, then let’s define it:

interface MenuContent {
}

Now that we have the main skeleton, let’s define some menu options to see exactly how we can link them.

class Type1MenuListModel : MenuContent {

var attribue1Type1: String? = ""
var attribute2Type1: String? = ""

}
class Type2MenuListModel : MenuContent {

var attribue1Type2: String? = ""
var attribute2Type2: String? = ""
var dateIndex: Int? = 0
var callOption: Boolean? = true

}

Ok. Now we have two types of menu, Type 1 and Type 2. Each of them is a MenuContent, but all of these have different content. A specific menu entry for Type 1 or Type 2 will look as shown below:

val menuListModelType1 = MenuListModel<Type1MenuListModel>()

Now we have everything prepared.

Time to get the list of options

In my case I have implemented within an Android project, therefore I will get the menu content from shared preferences. This is not a constraint because the result from shared preferences is a string so you can get it from all other places.

Let’s add something in the menu and save the values into a JSON file. Check the content of the file…everything should be there. Now let’s try to get the content of the file where we stored the JSON’s content.

Wow….error -> Oops, wtf is going on?

The error should say something like: RunTimeException: “Unable to invoke no-args constructor for interface MenuContent. Register an InstanceCreator with Gson for this type may fix this problem.”

Solution

The below solution is the subject of this post. What we have to do, to be able to use this generic solution is to define our generic Interface Adapter that will override serialize and deserialize methods.

Now, the second step is to register this adapter to the Gson configuration:

private var gson : Gson? = nullval gsonBuilder = GsonBuilder()
gsonBuilder.registerTypeAdapter(MenuContent::class.java, MenuContentInterfaceAdapter())
gson = gsonBuilder.create()

After this configuration everything should work fine.

Summary

Each menu has a different content based on it’s type. Taking this into consideration we have created an enumeration with possible menu types. ->MenuListType

Based on this approach each menu type will have a different data model. Basically all of these are menus, therefore the same interface will implement. ->MenuContent

The connection (and the logic in the same time) will be implemented into a different class which will use a factory pattern to return the right menu data.

The data model will be stored into a file using JSON format. For marshal and unmarshal we will use the same data model for each type. Because we are using a generic model to map data from file mentioned. -> MenuListModel<T : MenuContent>. We have to create our own interface adapter -> MenuContentInterfaceAdapter to serialize and deserialize the content.

Note: the CLASSNAME and DATA are defined by the Interface Adapter to be able to know on unmarsheling time on what object should be mapped the content of the data.

Just code it!

--

--