import com.google.gson.Gson import com.google.gson.GsonBuilder 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.JsonWriter import kotlin.jvm.internal.Reflection import kotlin.reflect.KClass object Json { val gson = GsonBuilder().registerTypeAdapterFactory( object : TypeAdapterFactory { override fun create(gson: Gson, type: TypeToken): TypeAdapter { val kclass = Reflection.getOrCreateKotlinClass(type.rawType) return if (kclass.sealedSubclasses.any()) { SealedClassTypeAdapter(kclass, gson) } else gson.getDelegateAdapter(this, type) } }).create() inline fun fromJson(x: String): T = this.gson.fromJson(x, T::class.java) fun fromJsonWithClass(x: String, classObj: Class): T = this.serializer.fromJson(x, classObj) inline fun toJson(item: T): String = this.gson.toJson(item) } sealed class SealedClassOne { data class D1(val name: String, val age: Int) : SealedClassOne() data class D2(val name: String, val salary: Int) : SealedClassOne() data class D3(val p: SealedClassOne) : SealedClassOne() } sealed class SealedClassTop { data class Container(val p: SealedClassOne) : SealedClassTop() object Singleton : SealedClassTop() } data class Container(val c: SealedClassTop) fun main() { val h1 = SealedClassOne.D1("Krishna", 34) val h2 = SealedClassOne.D2("Archana", 1233) println("Running serialization") val h1Json = Json.toJson(h1) println("JSON: $h1Json") val h2Json = Json.toJson(h2) println("JSON: $h2Json") println("Running deserialization") println(Json.fromJson(h1Json)) println(Json.fromJson(h2Json)) //super nested model! val l1 = SealedClassTop.Container(SealedClassOne.D3(SealedClassOne.D3(h1))) val l1Json = Json.toJson(l1) println("Json : $l1Json") val l1Back = Json.fromJson(l1Json) println("L1 : $l1Back") println("Are L1 Equal? : ${l1 == l1Back}") //more complex shit val container1 = Container(l1) val container2 = Container(SealedClassTop.Singleton) val container1Json = Json.toJson(container1) println("Json: $container1Json") val container2Json = Json.toJson(container2) println("Json: $container2Json") val container1Back = Json.fromJson(container1Json) println("Container match : ${container1 == container1Back}") val container2Back = Json.fromJson(container2Json) println("Singleton container -> ${container2Back.c}") println("Singleton match : ${container2 == container2Back}") val container3Back = Json.fromJson(container2Json) println("Extended! : $container3Back") } class SealedClassTypeAdapter(val kclass: KClass, val gson: Gson) : TypeAdapter() { override fun read(jsonReader: JsonReader): T? { jsonReader.beginObject() //start reading the object val nextName = jsonReader.nextName() //get the name on the object val innerClass = kclass.sealedSubclasses.firstOrNull { it.simpleName!!.contains(nextName) } ?: throw Exception("$nextName is not found to be a data class of the sealed class ${kclass.qualifiedName}") val x = gson.fromJson(jsonReader, innerClass.javaObjectType) jsonReader.endObject() //if there a static object, actually return that back to ensure equality and such! return innerClass.objectInstance as T? ?: x } override fun write(out: JsonWriter, value: T) { val jsonString = gson.toJson(value) out.beginObject() out.name(value.javaClass.canonicalName.splitToSequence(".").last()).jsonValue(jsonString) out.endObject() } }