У меня есть некоторые метаданные об объектах БД, которые я хотел бы отправить по проводам, включая идентификатор некоторой строки. Поскольку kotlin предлагает сериализацию Json из коробки, я вполне готов использовать это. Теперь проблема в том, что тип идентификатора может быть любого типа, на самом деле пока только Int, Long или UUID. Теперь у меня есть метаданные, которые описывают тип ключа, поэтому с обеих сторон я могу предоставить (де) сериализатор. Итак, у меня есть:
@file:ContextualSerialization(MetaData::class, Any::class)
package com.example
sealed class MetaProperty(val name:String, val type: KClass<*>) {
abstract fun serializer() : KSerializer<Any>
}
class IntProperty(name:String) : MetaProperty(name, Int::class) {
override fun serializer(): KSerializer<Any> = IntSerializer as KSerializer<Any>
}
class MetaData(val type:String, val idType:MetaProperty)
@Serializable
data class DBRelation(
val from:MetaData,
val fromId: Any,
val to:MetaData,
val toId:Any)
Теперь я просто хочу сериализовать Relation, на самом деле в моем случае это только сторона отношения (from не обязательна), я просто включил его, чтобы сделать возможной сериализацию. Я написал наивную реализацию пользовательского сериализатора:
@Serializer(forClass = DBRelation::class)
object RelationToSerializer : KSerializer<DBRelation> {
lateinit var meta : (String) -> MetaData
override val descriptor: SerialDescriptor = object : SerialClassDescImpl("relation") {
init {
addElement("from")
addElement("to")
addElement("fromId")
addElement("toId")
}
}
@ImplicitReflectionSerializer
override fun serialize(encoder: Encoder, obj: DBRelation) {
val comp = encoder.beginStructure(descriptor)
comp.encodeStringElement(descriptor, 0, obj.from.type)
comp.encodeSerializableElement(descriptor, 2, obj.from.idType.type.serializer() as KSerializer<Any>, obj.fromId)
}
override fun deserialize(decoder: Decoder): DBRelation = run {
var from : String? = null
var to : String? = null
var fromId : Any? = null
var toId : Any? = null
val dec = decoder.beginStructure(descriptor)
loop@ while (true) {
when(val idx = dec.decodeElementIndex(descriptor)) {
CompositeDecoder.READ_DONE -> break@loop
0 -> from = dec.decodeStringElement(descriptor, idx)
1 -> to = dec.decodeStringElement(descriptor, idx)
2 -> fromId = dec.decodeSerializableElement(descriptor, idx, meta(from?:throw IllegalStateException()).idType.serializer())
}
}
DBRelation(meta(from!!), fromId!!, meta(to!!), toId!!)
}
}
Теперь эта реализация может работать или не работать в зависимости от порядка десериализации полей.
Итак, у меня два вопроса:
A. Кто-нибудь знает другое решение для моей проблемы? Тот, который не требует от меня написания специальных классов Relation для каждой возможной комбинации типов ключей?
B. Еще лучше, есть ли способ сказать сериализатору удерживать значение идентификатора до десериализации from
? Или дать мне какую-нибудь сериализованную версию ID, которую я могу позже (как только у меня будет from
значение`) десериализовать?