Guide to JSON for Android Developer (Part 3)

Andrea
3 min readNov 7, 2022

This is the series of JSON lesson that I extracted from my projects. It consists of
Part 1 : Parsing data from Json to Object (deserialization)
Part 2 : Parsing data from Object to JSON (Serialization)
Part 3 : Create TypeConverter for ROOM Database

For ROOM database user, sometimes you need your app to store a custom data type in a single database column. You support custom types by providing type converters, which are methods that tell Room how to convert custom types to and from known types that Room can persist. You identify type converters by using the @TypeConverter annotation. And hence you need to provide the converter class for those data type.

  1. Custom Object
    For example, if we have custom data class for Location where:
data class Location(
val address: String,
val lat: Int,
val lng: Int
)

We can create converter class

class Converter {

@TypeConverter
fun fromLocation(value: Location?): String =
Gson().toJson(value)

@TypeConverter
fun toLocation(value: String): Location =
Gson().fromJson(value, Location::class.java)
}

2. Array List with Inheritance Class Object
For more complex object, let say if we have base class

abstract class Fields (
open var id : String? = null,
open var label : String? = null,
open var description : String? = null,
open var type : String? = null,
open var required : Boolean? = null,
)

And a lot of data class inherited from this base class

data class DateField (
var disableFuture : Boolean? = null,
var disablePast : Boolean? = null,
) : Fields()
data class CheckBoxField (
var group : ArrayList<Group> = arrayListOf(),
var minChecked: Int? = null,
var maxChecked: Int? = null
): Fields()
data class RatingField (
var maxStars : Int? = null,
) : Fields()

The converter class will be slightly complex. Each object must determine which “type” object is before convert it using Gson

class Converter {
@TypeConverter
fun fieldToJson(value: ArrayList<Fields>?): String = Gson().toJson(value)

@TypeConverter
fun jsonToField(value: String): java.util.ArrayList<Fields> {
val gson = Gson()
val returnArray = arrayListOf<Fields>()
val array = JsonParser.parseString(value).asJsonArray
array.forEach { jsonElement ->
val entry = jsonElement.asJsonObject
val type = entry.get("type").toString()
when {
type.contains("CheckBox Field") -> returnArray.add(gson.fromJson(jsonElement, CheckBoxField::class.java))
type.contains("Rating Field") -> returnArray.add(gson.fromJson(jsonElement, RatingField::class.java))
type.contains("Date Field") -> returnArray.add(gson.fromJson(jsonElement, DateField::class.java))
else -> throw IllegalArgumentException("Can't deserialize $entry")
}
}
return returnArray
}
}

3. Map Object with Inheritance Class
Similar thing happen if the inheritance class that we need are organize in Map type object. For each object inside the map, we need to cast it to common object and determine the object type before we can cast it to the correct object.

class Converter {

@TypeConverter
fun fromFieldsToJson(value: Map<String, Fields>?): String = Gson().toJson(value)

@TypeConverter
fun jsonToValue(json: String): Map<String, Fields> {
val returnMap = mutableMapOf<String, Value>()
val jsonObject = JsonParser.parseString(json).asJsonObject
val gson = Gson()
jsonObject.entrySet().forEach { map ->
val value = gson.fromJson(map.value, RatingField::class.java)
val type = value.type ?: ""
when {
type.contains("CheckBox Field") -> returnMap[map.key] =
gson.fromJson(map.value, CheckBoxValue::class.java)
type.contains("Rating Field) -> returnMap[map.key] =
gson.fromJson(map.value, RatingValue::class.java)
type.contains("Date Field) -> returnMap[map.key] =
gson.fromJson(map.value, DateValue::class.java)

else -> throw IllegalArgumentException("Can't deserialize ${map.key} ${map.value}")
}
}
return returnMap
}

}

This example is taken for open source WORX project. Feel free to add more complex example for Json manipulation.

--

--