Jackson @JsonSerialize注解在Kotlin数据类新增属性时未生效
问题描述
我定义了两个Kotlin数据类Test1和Test2,二者的LocalDate类型date字段都添加了@JsonSerialize(using = ToStringSerializer::class)注解,Test2额外包含一个Duration类型的duration属性。
通过注册Kotlin模块与JavaTime模块的ObjectMapper进行序列化测试后发现:Test1的date字段正常序列化为字符串格式,而Test2的date字段被序列化为对象结构。当移除Test2的duration属性或将其类型改为String时,注解可正常生效。
相关代码
data class Test1(@JsonSerialize (using = ToStringSerializer::class) val date: LocalDate, val number: Int) data class Test2(@JsonSerialize (using = ToStringSerializer::class) val date: LocalDate, val number: Int, val duration: Duration) class SerializeTest { @Test fun test(){ val mapper = ObjectMapper().apply{ enable(SerializationFeature.INDENT_OUTPUT) registerModule(kotlinModule()) registerModule(JavaTimeModule()) } val x = Test1(Clock.System.todayAt(TimeZone.UTC), 123) val y = Test2(Clock.System.todayAt(TimeZone.UTC), 1, 1.seconds) println(mapper.writeValueAsString(x)) println(mapper.writeValueAsString(y)) } }
(注:LocalDate和Duration为Kotlin类型,非Java类型)
序列化输出结果
{ "date" : { "year" : 2022, "dayOfMonth" : 26, "month" : "DECEMBER", "dayOfWeek" : "MONDAY", "dayOfYear" : 360, "monthNumber" : 12, "value$kotlinx_datetime" : [ 2022, 12, 26 ] }, "number" : 1, "duration" : 2000000000 } { "date" : "2022-12-26", "number" : 123 }
原因分析
这个问题的核心是Jackson模块的序列化策略优先级冲突,具体逻辑如下:
- 你使用的
LocalDate和Duration是kotlinx-datetime库的类型,而非Java原生java.time包下的类型,JavaTimeModule本身并不处理这类Kotlin专属时间类型。 - 对于
Test1,由于没有特殊类型字段,Jackson会优先遵循字段上的@JsonSerialize注解,调用ToStringSerializer将LocalDate序列化为字符串。 - 当
Test2包含Duration字段时,jackson-module-kotlin会触发针对整个类的默认序列化策略:它会优先通过Kotlin数据类的属性访问器序列化所有字段,直接忽略字段上的局部注解配置。这是因为模块在处理kotlinx-datetime.Duration这类未被其他模块覆盖的特殊类型时,会切换到更激进的默认序列化模式,覆盖了注解的优先级。 - 移除
Duration字段或改为String类型后,类中不再存在需要特殊处理的类型,局部的@JsonSerialize注解会重新获得优先级,恢复正常工作。
解决建议
- 替换
JavaTimeModule为专门的kotlinx-datetime-jackson模块,它能正确处理所有kotlinx-datetime类型,同时尊重字段上的序列化注解。 - 若必须保留现有模块配置,可以给
Test2的date字段额外添加@JsonFormat(shape = JsonFormat.Shape.STRING)注解,强制Jackson将其序列化为字符串,覆盖默认策略。
内容的提问来源于stack exchange,提问作者wasmachien
相关产品推荐
相关产品推荐

