【问题标题】:Using a KClass reference as a reified parameter to deserialize from JSON使用 KClass 引用作为具体参数从 JSON 反序列化
【发布时间】:2021-01-23 15:03:17
【问题描述】:

我正在尝试实现一个通用的序列化框架,以使用 kotlinx 序列化将传出和传入的消息转换为 json。我正在开发一个多平台应用程序,所以我试图让它在 KotlinJVM 和 KotlinJS 上运行。

为此,我为每条消息添加了一个type 字段,并使用一个映射将每个type 字符串映射到KClass。那张地图的类型是什么?它包含 KClass<> 对象,其类扩展了 Message 类,因此在 java 中我将我的地图指定为 Map<KClass<? extends Message>, String>.

我如何在 Kotlin 中做到这一点?

之后我需要根据消息的键和类型对消息进行序列化和反序列化。 Java 框架为我想要反序列化/实例化的对象的类型(例如 gson.fromJson(ClientMessage.class))采用 Class 参数。在 Kotlin 中,这是使用 reified 参数 Json.decodeFromString<Type> 完成的。我在编译时不知道消息的类型,只是引用了KClass,我怎样才能基于它实例化一个对象?

@Serializable
open class Message(val type: String) {

    companion object {
        val messageTypes: Map<KClass<out Message>, String> = mapOf(
            ClientLoginMessage::class to "clientLoginMessage",
            Message::class to "message"
        )

        inline fun <reified T> getMessageTypeByClass(): String = messageTypes[T::class]!! // utility for defining the type in the constructors of the individual messages
    }

    fun toJson() = Json.encodeToString(this)

    fun fromJson(json: String): Message? {
        val plainMessage = Json.decodeFromString<Message>(json) // get type string from json
        return messageTypes.entries.find { it.value == plainMessage.type }?.let {
            // how can I use the KClass from it.key as reified parameter?
            Json.decodeFromString<?????>(json) 
        }
    }
}

@Serializable
class ClientLoginMessage
         : Message(Message.getMessageTypeByClass<ClientLoginMessage>()) {}

【问题讨论】:

    标签: json kotlin generics kotlin-js kotlinx.serialization


    【解决方案1】:

    为类型创建一个序列化器的映射:

    val serializers: Map<KClass<out Message>, KSerializer<out Message>> = mapOf(
                ClientLoginMessage::class to ClientLoginMessage.serializer(),
                Message::class to Message.serializer()
            )
    
    
    

    将所需的序列化程序传递给Json.decodeFromString,如下所示:

    fun fromJson(json: String): Message? {
            val plainMessage = Json.decodeFromString<Message>(json) // get type string from json
            return messageTypes.entries.find { it.value == plainMessage.type }?.let {
                // how can I use the KClass from it.key as reified parameter?
                Json.decodeFromString(serializers.get(plainMessage.type)!!, json)
            }
        }
    

    您可能还想看看 Kotlin 内置的多态类处理:https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md

    【讨论】:

    • 我最终只是按照你的建议使用了内置的多态性,从那时我阅读文档时就完全忘记了这一点。感谢您仍然回答我关于如何为任意类执行此操作的原始问题。
    猜你喜欢
    • 2017-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多