Skip to content

Instantly share code, notes, and snippets.

@tokou
Created October 29, 2018 19:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tokou/1660906bd103b0a68f2da283a9c6522c to your computer and use it in GitHub Desktop.
Save tokou/1660906bd103b0a68f2da283a9c6522c to your computer and use it in GitHub Desktop.
Parent serializer
sealed class Parent {
@Serializable
data class B(val b: String) : Parent()
@Serializable
data class A(val a: String) : Parent()
}
object ParentSerializer : KSerializer<Parent> {
override val descriptor: SerialDescriptor = SerialClassDescImpl("Parent")
override fun deserialize(input: Decoder): Parent {
val jsonReader = input as? JSON.JsonInput
?: throw SerializationException("This class can be loaded only by JSON")
val tree = jsonReader.readAsTree() as? JsonObject
?: throw SerializationException("Expected JSON object")
val mapper = JsonTreeMapper()
return when (val t = tree["t"].primitive.content) {
"a" -> mapper.readTree(tree, Parent.A.serializer())
"b" -> mapper.readTree(tree, Parent.B.serializer())
else -> throw SerializationException("Unknown type $t")
}
}
override fun serialize(output: Encoder, obj: Parent) {
val jsonWriter = output as? JSON.JsonOutput
?: throw SerializationException("This class can be saved only by JSON")
val mapper = JsonTreeMapper()
val tree = when (obj) {
is Parent.A -> mapper.writeTree(obj, Parent.A.serializer())
is Parent.B -> mapper.writeTree(obj, Parent.B.serializer())
}
val k = when (obj) {
is Parent.A -> "a"
is Parent.B -> "b"
}
val o = tree.jsonObject
val c: MutableMap<String, JsonElement> = mutableMapOf("t" to JsonPrimitive(k))
c.putAll(o.content)
val r = o.copy(c)
jsonWriter.writeTree(r)
}
}
class ParentSerializerTest {
@Test
fun serialize() {
val strA = JSON.stringify(ParentSerializer, Parent.A("hello"))
val strB = JSON.stringify(ParentSerializer, Parent.B("toto"))
assertEquals("""{"t": "a", "a": "hello"}""", strA)
assertEquals("""{"t": "b", "b": "toto"}""", strB)
}
@Test
fun deserialize() {
val str = """{"t": "a", "a": "bibi"}"""
val o = JSON.parse(ParentSerializer, str)
assertEquals(Parent.A("bibi"), o)
}
}
open class PolymorphicSerializer<T : Any>(
name: String,
private val typeKey: String,
private val keys: Map<KClass<out T>, String>
) : KSerializer<T> {
private val classes = keys.reversed()
override val descriptor: SerialDescriptor = SerialClassDescImpl(name)
@ImplicitReflectionSerializer
override fun deserialize(input: Decoder): T {
val jsonReader = input as? JSON.JsonInput
?: throw SerializationException("This class can be loaded only by JSON")
val tree = jsonReader.readAsTree() as? JsonObject
?: throw SerializationException("Expected JSON object")
val mapper = JsonTreeMapper()
val t = tree[typeKey].primitive.content
val k = classes[t]
val s = k?.serializer()
if (s != null) return mapper.readTree(tree, s)
else throw SerializationException("No serializer found for type $t")
}
@ImplicitReflectionSerializer
override fun serialize(output: Encoder, obj: T) {
val jsonWriter = output as? JSON.JsonOutput
?: throw SerializationException("This class can be saved only by JSON")
val mapper = JsonTreeMapper()
val k = obj::class as KClass<T>
val tree = mapper.writeTree(obj, k.serializer())
val l = keys[k]
val o = tree.jsonObject
val c: MutableMap<String, JsonElement> = mutableMapOf(typeKey to JsonPrimitive(l))
c.putAll(o.content)
val r = o.copy(c)
jsonWriter.writeTree(r)
}
}
class PolymorphicSerializerTest {
private val serializer = PolymorphicSerializer("Parent", "type", mapOf(
Parent.A::class to "a",
Parent.B::class to "b"
))
@Test
fun polymorphicSerialize() {
val strA = JSON.stringify(serializer, Parent.A("hello"))
val strB = JSON.stringify(serializer, Parent.B("toto"))
assertEquals("""{"type": "a", "a": "hello"}""", strA)
assertEquals("""{"type": "b", "b": "toto"}""", strB)
}
@Test
fun polymorphicDeserialize() {
val str = """{"type": "a", "a": "bibi"}"""
val o = JSON.parse(serializer, str)
assertEquals(Parent.A("bibi"), o)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment