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.
相关产品推荐
相关产品推荐

