Spring Boot中Kotlin Jackson嵌套JSON反序列化报错问题
Kotlin Jackson反序列化嵌套JSON的类型转换错误解决办法
问题背景
在Spring Boot中用Jackson反序列化嵌套JSON到Kotlin数据类时,遇到报错:
Cannot construct instance of
...C(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('test')
对应的JSON结构:
{ "values":[ { "name":"test", "map":{ "d1":"d1Value", "d2":"d2Value", "d3":"d3Value" } }, { "name":"test1", "map":{ "d1":"d1Value", "d4":"d4Value" } } ] }
使用的Kotlin数据类:
data class A( val values: List<B>, ) data class B( val name: C, val map: Map<D, String>, ) data class C(@JsonValue val value: String) data class D(@JsonValue val value: String)
问题原因
@JsonValue注解仅用于序列化时将对象转为字符串,但反序列化时,Jackson不知道如何从字符串实例化C、D这类数据类——Kotlin数据类的主构造器默认不会被Jackson自动识别为反序列化的构造逻辑入口。
解决方法
方法1:给数据类添加@JsonCreator注解
直接在C、D的主构造器上标注@JsonCreator,告诉Jackson用这个构造器从字符串创建实例:
data class A( val values: List<B>, ) data class B( val name: C, val map: Map<D, String>, ) // 给主构造器添加@JsonCreator data class C @JsonCreator constructor(@JsonValue val value: String) data class D @JsonCreator constructor(@JsonValue val value: String)
方法2:用伴生对象的工厂方法配合@JsonCreator
如果不想修改主构造器,可以在伴生对象里写工厂方法,并用@JsonCreator标记:
data class C(@JsonValue val value: String) { companion object { @JsonCreator fun fromString(value: String): C = C(value) } } data class D(@JsonValue val value: String) { companion object { @JsonCreator fun fromString(value: String): D = D(value) } }
方法3:确保Jackson Kotlin模块正确注册
Spring Boot通常会自动注册Kotlin模块,但如果是手动创建ObjectMapper,需要显式添加模块,让Jackson能识别Kotlin数据类的构造参数:
val mapper = jacksonObjectMapper().registerModule(KotlinModule.Builder().build()) val a = mapper.readValue<A>(json)
或者在Spring配置中自定义ObjectMapper:
@Configuration class JacksonConfig { @Bean fun objectMapper(): ObjectMapper { return ObjectMapper().registerModule(KotlinModule()) } }
验证示例
修正后运行反序列化代码,即可正常解析JSON:
val json = """ { "values":[ { "name":"test", "map":{ "d1":"d1Value", "d2":"d2Value", "d3":"d3Value" } }, { "name":"test1", "map":{ "d1":"d1Value", "d4":"d4Value" } } ] } """.trimIndent() val mapper = jacksonObjectMapper().registerModule(KotlinModule()) val result = mapper.readValue<A>(json) println(result)
内容的提问来源于stack exchange,提问作者J.Doe
相关产品推荐
相关产品推荐

