You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin DTO必须用Data类吗?非Data类DTO接口请求报错排查

Kotlin中DTO必须是Data类吗?问题排查与解决

结论先行:DTO不需要必须是Data类

Data类只是Kotlin提供的语法糖,自动生成equals()、hashCode()、toString()、copy()等方法,适合简单不可变的DTO场景。当需要基于基础DTO扩展子类时,确实不能用Data类(Data类不允许被继承),此时使用密封类、抽象类、开放类完全可行,但要注意JSON序列化/反序列化的规则。


你的代码报错原因

当前的ProjectDTO是密封类,密封类属于抽象类型,无法直接实例化。Spring默认使用Jackson处理JSON请求的序列化,当@RequestBody指定类型为ProjectDTO时,Jackson尝试创建该类的实例,但密封类不能被直接实例化,且你没有定义任何具体子类,也没有配置Jackson识别子类的规则,因此抛出类型定义错误。


解决方案

方案1:改用抽象类+具体子类(最直接)

如果不需要密封类的特性(比如限制子类范围),将基础DTO改为抽象类,然后定义具体的子类用于接收请求:

// 基础抽象DTO,定义通用字段和验证逻辑
internal abstract class ProjectDTO(
    open var _id: ObjectId?,
    open var name: String,
    open var country: String,
    open var sicCode: String,
    open var sicDescription: String,
    open var screenType: String?,
) {
    init {
        // 这里添加你的验证逻辑,比如非空检查
        require(name.isNotBlank()) { "项目名称不能为空" }
    }
}

// 用于接收创建请求的具体DTO子类
internal class CreateProjectDTO(
    override var _id: ObjectId? = null,
    override var name: String,
    override var country: String,
    override var sicCode: String,
    override var sicDescription: String,
    override var screenType: String?,
) : ProjectDTO(_id, name, country, sicCode, sicDescription, screenType)

修改Controller接收具体子类:

@PostMapping
suspend fun createProject(@RequestBody request: CreateProjectDTO): ResponseEntity<ProjectDTO> {
    return withContext(Dispatchers.IO) {
        ResponseEntity(projectService.createProject(request), HttpStatus.CREATED)
    }
}

此时原请求JSON无需修改,Jackson可以正常实例化CreateProjectDTO对象。

方案2:保留密封类并配置Jackson支持

如果必须使用密封类(比如需要限制子类只能在特定包内定义),需要:

  1. 定义密封类的具体子类
  2. 配置Jackson识别子类的注解
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo

// 配置Jackson通过"type"字段识别子类
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "type" // 用于区分子类的字段名
)
@JsonSubTypes(
    // 注册具体子类,指定type字段对应的值
    JsonSubTypes.Type(value = ConcreteProjectDTO::class, name = "create")
)
internal sealed class ProjectDTO(
    open var _id: ObjectId?,
    open var name: String,
    open var country: String,
    open var sicCode: String,
    open var sicDescription: String,
    open var screenType: String?,
) {
    init {
        // 验证逻辑
    }
}

// 密封类的具体子类
internal class ConcreteProjectDTO(
    override var _id: ObjectId? = null,
    override var name: String,
    override var country: String,
    override var sicCode: String,
    override var sicDescription: String,
    override var screenType: String?,
) : ProjectDTO(_id, name, country, sicCode, sicDescription, screenType)

修改请求JSON,添加type字段指定子类:

{
    "type": "create",
    "name": "testDTO",
    "country": "testDTO",
    "sicCode": "testDTO",
    "sicDescription": "testDTO",
    "screenType": "testDTO"
}

Controller可以保持原代码不变,Jackson会根据type字段自动实例化对应的子类。


额外注意事项

确保你的项目中引入了jackson-module-kotlin依赖,Spring Boot项目通常会自动包含该依赖,它能更好地支持Kotlin的特性(比如默认参数、数据类等)。

内容的提问来源于stack exchange,提问作者Sachin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 21:44:58