Как я могу сериализовать самостоятельную структуру с помощью сериализации kotlin kotlinx? - PullRequest
1 голос
/ 06 ноября 2019

У меня есть некоторые метаданные об объектах БД, которые я хотел бы отправить по проводам, включая идентификатор некоторой строки. Поскольку 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 значение`) десериализовать?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...