0

0

Kotlinx.Serialization接口多态序列化深度解析与实践

DDD

DDD

发布时间:2025-10-16 11:34:14

|

912人浏览过

|

来源于php中文网

原创

Kotlinx.Serialization接口多态序列化深度解析与实践

本文深入探讨kotlinx.serialization中处理接口多态序列化时常见的“class is not registered for polymorphic serialization”错误。核心解决方案是避免在接口上直接使用`@serializable`注解,而是通过`serializersmodule`注册接口的所有具体实现类,并配置`json`实例以启用多态序列化,从而确保不同数据类能通过同一接口进行正确序列化与反序列化。

理解Kotlinx.Serialization中的多态序列化

面向对象编程中,多态性允许我们通过一个父类型(如接口或抽象类)的引用来操作不同子类型的对象。当涉及到数据序列化时,"多态序列化"指的是将一个父类型变量所引用的具体子类型对象正确地转换为可存储或传输的格式,并在反序列化时能将其恢复为正确的子类型对象。

Kotlinx.Serialization库默认情况下,对于data class等具体类型能够自动推断并生成序列化器。然而,当尝试序列化一个接口类型时,序列化器并不知道该接口背后可能存在哪些具体的实现类。直接在接口上使用@Serializable注解,实际上是告诉序列化器“请为这个接口生成一个序列化器”,但这在逻辑上是行不通的,因为接口本身没有具体的数据结构可供序列化。

错误示例分析

考虑以下代码结构,其中Todo是一个接口,而userDataForRegistration、userDataForLogin等是其具体实现:

@Serializable // 错误:不应在接口上使用此注解
interface Todo{}

@Serializable
data class userDataForRegistration(val name: String, val number: String, val password: String): Todo

@Serializable
data class userDataForLogin(val number: String, val password: String): Todo

@Serializable
data class contactForRemove(val id: String, val number: String): Todo

// ... 其他实现类

当尝试序列化一个Todo类型的变量时,例如:

val userDataForLogin = userDataForLogin("test_user", "123456", "password")
val jsonString = Json.encodeToString(userDataForLogin) // 假设这里的Json是默认配置

如果Json实例没有正确配置多态序列化,或者如原始问题所示,当方法签名接受Todo类型时:

fun sendData(url: String, param: String, body: Todo){
    var json = Json.encodeToString(body) // 在这里发生错误
    // ...
}

就会遇到类似以下错误信息:

@Serializable annotation is ignored because it is impossible to serialize automatically interfaces or enums. Provide serializer manually via e.g. companion object

这个错误明确指出,@Serializable注解不能直接用于接口,因为Kotlinx.Serialization无法自动为其生成序列化器。它需要我们手动提供序列化器,或者更准确地说,是告知它如何处理接口的多态性。

网易人工智能
网易人工智能

网易数帆多媒体智能生产力平台

下载

正确的处理方式:SerializersModule与多态注册

解决此问题的核心在于:移除接口上的@Serializable注解,并通过SerializersModule明确告知Kotlinx.Serialization,当遇到某个接口类型时,其可能有哪些具体的实现类。

步骤一:移除接口上的@Serializable注解

接口Todo不应带有@Serializable注解。它仅作为类型契约存在。

interface Todo // 移除 @Serializable

步骤二:在实现类上保留@Serializable注解

所有具体的数据类(如userDataForRegistration、userDataForLogin等)仍然需要@Serializable注解,以便Kotlinx.Serialization能为其生成默认的序列化器。

@Serializable
data class userDataForRegistration(val name: String, val number: String, val password: String): Todo

@Serializable
data class userDataForLogin(val number: String, val password: String): Todo

@Serializable
data class contactForRemove(val id: String, val number: String): Todo

// ... 其他实现类

步骤三:创建并配置SerializersModule

SerializersModule是Kotlinx.Serialization中用于注册自定义序列化器和多态类型映射的机制。我们需要创建一个模块,并在其中使用polymorphic函数来定义接口的多态序列化规则,再通过subclass函数注册每个具体的实现类。

import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
import kotlinx.serialization.json.Json

// 定义一个全局或模块级别的SerializersModule
val appSerializersModule = SerializersModule {
    // 为Todo接口注册多态序列化规则
    polymorphic(Todo::class) {
        // 注册Todo接口的所有具体实现类
        subclass(userDataForRegistration::class)
        subclass(userDataForLogin::class)
        subclass(contactForRemove::class)
        // 确保所有可能通过Todo接口序列化的类都在这里注册
    }
    // 如果有其他接口或抽象类需要多态序列化,可以在这里继续添加
    // polymorphic(AnotherInterface::class) { ... }
}

// 配置一个使用此SerializersModule的Json实例
val configuredJson = Json {
    serializersModule = appSerializersModule // 将模块应用到Json实例
    prettyPrint = true // 格式化输出,便于阅读
    ignoreUnknownKeys = true // 忽略JSON中存在但数据类中不存在的字段
    encodeDefaults = true // 序列化时包含默认值
}

步骤四:在序列化/反序列化时使用配置好的Json实例

在你的Connection类或其他需要进行序列化/反序列化操作的地方,确保使用上述配置好的configuredJson实例。

import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import java.io.IOException
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.decodeFromString

class Connection {
    val client = OkHttpClient()

    // 使用配置好的Json实例
    private val json = configuredJson // 使用之前定义并配置好的json实例

    fun sendData(url: String, param: String, body: Todo){
        // 使用配置好的json实例进行序列化
        val jsonString = json.encodeToString(body)
        val reqBody = RequestBody.create("application/json; charset=utf-8".toMediaTypeOrNull(), jsonString)

        val request = Request.Builder()
            .url(url)
            .post(reqBody)
            .build()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                println("error" + e)
            }
            override fun onResponse(call: Call, response: Response){
                val res = response.body?.string()
                when(param){
                    "login", "registration" -> {
                        try{
                            // 如果User也是一个接口并需要多态反序列化,同样需要配置其SerializersModule
                            // 否则,如果User是具体类,则直接使用即可
                            // 注意:这里需要确保res包含类型信息,或者User是预期的具体类型
                            // val objUser = json.decodeFromString(res.toString())
                            // returnUser(objUser)

                            // 示例:如果返回的是Message类型
                            val mes = json.decodeFromString(res.toString())
                            returnMessage(mes)
                        }
                        catch(e: Exception){
                            val mes = json.decodeFromString(res.toString())
                            returnMessage(mes)
                        }
                    }
                    "contact" ->{
                        val mes = json.decodeFromString(res.toString())
                        returnMessage(mes)
                    }
                }
            }
        })
    }

    // 假设的返回处理函数
    fun returnUser(user: User) { println("User received: $user") }
    fun returnMessage(msg: message) { println("Message received: ${msg.message}") }
}

// 假设的User和message数据类
@Serializable data class User(val id: String, val name: String)
@Serializable data class message(val message: String)

// 调用示例
fun main() {
    val connection = Connection()
    val userDataForLogin = userDataForLogin("1234567890", "mypassword")
    // 调用sendData方法,此时body会被正确地多态序列化
    connection.sendData("http://localhost:8080/user/login", "login", userDataForLogin)
}

注意事项

  1. Json实例的统一性: 确保在整个应用中用于多态序列化和反序列化的Json实例都配置了相同的SerializersModule。在大型应用中,通常会创建一个单例或依赖注入的Json实例。
  2. 所有实现类的注册: 任何可能通过接口类型进行序列化或反序列化的具体类都必须在SerializersModule的polymorphic块中注册。如果遗漏了某个类,当尝试序列化该类时,仍然会遇到错误。
  3. @Serializable注解: 具体实现类(如data class)本身仍然需要@Serializable注解,这是生成其自身序列化器的基础。
  4. 反序列化时的类型提示: 当反序列化回接口类型时,Kotlinx.Serialization为了知道要实例化哪个具体类型,通常会在JSON中添加一个特殊的字段(默认为type)。例如,序列化userDataForLogin可能会生成类似{"type": "userDataForLogin", "number": "1234567890", "password": "mypassword"}的JSON。这意味着:
    • 服务器端在返回需要多态反序列化的数据时,也应该包含这个类型信息。
    • 如果服务器返回的JSON不包含type字段,你可能需要自定义classDiscriminator,或者在反序列化时明确指定期望的具体类型(如果上下文允许)。
  5. 插件版本: 确保你的build.gradle中org.jetbrains.kotlin.plugin.serialization插件版本是最新且兼容的。例如,1.6.21版本是支持此功能的。

总结

处理Kotlinx.Serialization中接口的多态序列化,关键在于理解其工作原理并非直接注解接口,而是通过SerializersModule为Json实例提供接口与具体实现类之间的映射关系。通过移除接口上的@Serializable注解,并在SerializersModule中注册所有相关实现类,我们可以有效地解决“Class is not registered for polymorphic serialization”错误,实现灵活且强大的多态数据处理能力。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

420

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

536

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

312

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

77

2025.09.10

go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

56

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

52

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

15

2025.11.27

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

15

2025.11.27

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

33

2026.01.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 8万人学习

Java 教程
Java 教程

共578课时 | 53.9万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号