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

Micronaut Serde与带默认值的Kotlin数据类反序列化问题

Micronaut 切换 Serde-Jackson 后的序列化问题解决方案

问题背景

在Micronaut 3.9.x及4.0.0版本中,将序列化组件从micronaut-jackson-databind切换为micronaut-serde-jackson后,遇到以下问题:

1. Kotlin数据类默认值反序列化报错

当反序列化JSON到带有默认值字段的Kotlin数据类时,若JSON中缺失对应字段会触发非空参数错误,而原jacksonObjectMapper会自动使用默认值。

示例数据类:

@Serdeable
data class MainAggregationVm(
    val someProp: List<ClassificationAndStats> = emptyList()
)

错误信息:

io.micronaut.http.client.exceptions.HttpClientResponseException: Error decoding HTTP response body: Error decoding JSON stream for type ...
... problem: Parameter specified as non-null is null

2023-7-23 更新问题

  • 库项目中有50多个数据类,需在7个应用中逐一添加@SerdeImport注解,工作量极大
  • 每个枚举类必须手动实现TypeConverter才能完成字符串到枚举的转换,示例如下:

枚举类:

enum class Aggregation(private val descripton: String) {
    SOMETHING_1("Some description #1"),
    SOMETHING_2("Some description #2");

    companion object {
        fun from(value: String): Aggregation = ... // 自定义映射逻辑
    }
}

手动实现的转换器:

@Singleton
class AggregationConverter : TypeConverter<String, Aggregation> {
    override fun convert(
        value: String,
        targetType: Class<Aggregation>,
        context: ConversionContext
    ): Optional<Aggregation> = Optional.of(Aggregation.from(value))
}

解决方案建议

1. 修复Kotlin数据类默认值反序列化问题

  • 配置正确依赖与插件:在项目构建脚本(如build.gradle.kts)中,添加micronaut-serde-kotlin编译器插件并启用注解处理器:
    plugins {
        id("io.micronaut.application") version "4.0.0"
        kotlin("jvm") version "1.9.0"
        kotlin("plugin.serialization") version "1.9.0"
    }
    
    dependencies {
        implementation("io.micronaut.serde:micronaut-serde-jackson")
        implementation("io.micronaut.serde:micronaut-serde-kotlin")
    }
    
    micronaut {
        runtime("netty")
        processing {
            incremental(true)
            annotations("your.package.**")
        }
    }
    
  • 标记字段为可选:在数据类的默认值字段上添加@Optional注解,告知Serde该字段允许缺失并使用默认值:
    @Serdeable
    data class MainAggregationVm(
        @Optional val someProp: List<ClassificationAndStats> = emptyList()
    )
    
  • 启用宽松反序列化模式:在application.yml中配置Serde允许缺失字段:
    micronaut:
      serde:
        deserialization:
          strict: false
    

2. 批量导入数据类减少重复配置

无需在每个应用中单独添加@SerdeImport,可在库项目中创建全局配置类,批量导入所有需要序列化的数据类:

@SerdeImport(
    classes = [
        MainAggregationVm::class,
        ClassificationAndStats::class,
        // 其他50多个数据类...
    ]
)
@Singleton
class LibrarySerdeConfig

将该配置类放在库项目的核心包下,所有依赖该库的应用会自动识别并应用这些导入配置。

3. 简化枚举类转换逻辑

无需为每个枚举类编写单独的TypeConverter,可通过以下方式优化:

  • 使用Serde枚举注解:在枚举类上添加@Serdeable.Enum,指定自定义反序列化逻辑:
    @Serdeable.Enum(deserializable = AggregationDeserializer::class)
    enum class Aggregation(private val descripton: String) {
        SOMETHING_1("Some description #1"),
        SOMETHING_2("Some description #2");
    
        companion object {
            fun from(value: String): Aggregation = ...
        }
    }
    
    class AggregationDeserializer : EnumDeserializer<Aggregation> {
        override fun deserialize(value: String, context: DeserializationContext): Aggregation {
            return Aggregation.from(value)
        }
    }
    
  • 通用枚举转换器:编写基于反射的通用转换器,自动调用枚举的from方法,适用于所有遵循相同规范的枚举:
    @Singleton
    class GenericEnumConverter : TypeConverter<String, Enum<*>> {
        override fun convert(
            value: String,
            targetType: Class<Enum<*>>,
            context: ConversionContext
        ): Optional<Enum<*>> {
            return try {
                val fromMethod = targetType.getDeclaredMethod("from", String::class.java)
                val enumValue = fromMethod.invoke(null, value) as Enum<*>
                Optional.of(enumValue)
            } catch (e: Exception) {
                Optional.empty()
            }
        }
    }
    
    注意:该通用转换器要求所有枚举类都有from(String)静态方法;若枚举命名与字符串完全一致,可直接依赖Serde默认的枚举反序列化逻辑,无需额外配置。

内容的提问来源于stack exchange,提问作者Roar S.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 17:27:48