Skip to main content

How to handle JSON with polymorphic types using GSON in Kotlin Android

How to handle JSON with polymorphic types using GSON in Kotlin Android.

In this tutorial, we will learn how to handle JSON with polymorphic types using the GSON library in Kotlin for Android development. Polymorphic types refer to a scenario where a JSON object can have multiple different types based on a certain condition or property.

Step 1: Add GSON dependency to your project

First, you need to add the GSON dependency to your build.gradle file:

dependencies {
implementation 'com.google.code.gson:gson:2.8.7'
}

Sync your project to make sure the dependency is downloaded and available for use.

Step 2: Define your JSON data classes

Next, you need to define your data classes that represent the JSON structure. Let's consider an example where we have a Shape class hierarchy with Circle, Rectangle, and Triangle subclasses.

sealed class Shape

data class Circle(val radius: Double) : Shape()
data class Rectangle(val width: Double, val height: Double) : Shape()
data class Triangle(val base: Double, val height: Double) : Shape()

In this example, Shape is declared as a sealed class, which means all its subclasses must be declared within the same file. Each subclass represents a specific shape with its corresponding properties.

Step 3: Create a custom GSON TypeAdapter

To handle polymorphic types, we need to create a custom TypeAdapter for GSON. A TypeAdapter is responsible for serializing and deserializing JSON objects to and from Java/Kotlin objects.

import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter

class ShapeTypeAdapterFactory : TypeAdapterFactory {
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (!Shape::class.java.isAssignableFrom(type.rawType)) {
return null
}

return ShapeTypeAdapter(gson) as TypeAdapter<T>
}
}

class ShapeTypeAdapter(private val gson: Gson) : TypeAdapter<Shape>() {
override fun write(out: JsonWriter?, value: Shape?) {
// Implement serialization if needed
}

override fun read(reader: JsonReader?): Shape? {
if (reader == null) {
return null
}

if (reader.peek() == JsonToken.NULL) {
reader.nextNull()
return null
}

reader.beginObject()
reader.nextName() // Assuming the shape type is specified as a property

val shapeType = reader.nextString()
val shape = when (shapeType) {
"Circle" -> gson.fromJson(reader, Circle::class.java)
"Rectangle" -> gson.fromJson(reader, Rectangle::class.java)
"Triangle" -> gson.fromJson(reader, Triangle::class.java)
else -> null
}

reader.endObject()
return shape
}
}

In this example, we create a custom ShapeTypeAdapterFactory that implements the TypeAdapterFactory interface. It checks if the given type is assignable to Shape and returns a ShapeTypeAdapter instance if true.

The ShapeTypeAdapter is responsible for deserializing JSON objects into the corresponding Shape subclasses. It reads the shape type from the JSON object and uses gson.fromJson to deserialize the remaining properties based on the shape type.

Step 4: Register the custom TypeAdapterFactory

To make GSON aware of our custom TypeAdapterFactory, we need to register it when creating a GSON instance.

val gson = GsonBuilder()
.registerTypeAdapterFactory(ShapeTypeAdapterFactory())
.create()

In this example, we create a new GsonBuilder and register our ShapeTypeAdapterFactory using the registerTypeAdapterFactory method. Finally, we call create to build the GSON instance.

Step 5: Serialize and deserialize JSON with polymorphic types

Now that we have set up the GSON configuration, we can use it to serialize and deserialize JSON objects that contain polymorphic types.

// Serializing a Shape object to JSON
val circle = Circle(5.0)
val json = gson.toJson(circle)
println(json) // Output: {"radius":5.0}

// Deserializing JSON to a Shape object
val json = "{\"radius\":5.0}"
val shape = gson.fromJson(json, Shape::class.java)
println(shape) // Output: Circle(radius=5.0)

In this example, we serialize a Circle object to JSON using gson.toJson, and then deserialize it back to a Shape object using gson.fromJson.

Conclusion

In this tutorial, we learned how to handle JSON with polymorphic types using the GSON library in Kotlin for Android development. We created a custom TypeAdapter and TypeAdapterFactory to handle the serialization and deserialization of polymorphic types. By following these steps, you can easily handle complex JSON structures with polymorphic types in your Android applications.