使用Kotlin和Spring Boot实现通用更新端点及复杂属性反序列化问题
解决方案
首先确认你使用的ObjectMapper是Spring容器注入的实例,不要手动创建,Spring Boot默认会自动将引入的Kotlin模块注册到容器托管的ObjectMapper中。
修正后的接口代码如下:
@PatchMapping(ENTITY_URL) fun update(@PathVariable("id") entity: E, @RequestBody fields: Map<String,Any>): E { entity::class.memberProperties .filterIsInstance<KMutableProperty<*>>() .forEach { prop -> fields[prop.name]?.let { fieldValue -> // 构造带泛型信息的目标类型,支持Kotlin原生类型、集合、自定义实体 val targetType = objectMapper.typeFactory.constructType(prop.returnType.javaType) // 直接完成Jackson内部结构到目标类型的转换,无需序列化后再反序列化 val typedValue = objectMapper.convertValue<Any>(fieldValue, targetType) prop.setter.call(entity, typedValue) } } repo.save(entity) return entity }
核心修改点说明
- 替换
readValue为convertValue方法:@RequestBody解析出来的fields中存储的是Jackson已经处理过的结构化数据(集合为ArrayList、对象为LinkedHashMap),转成字符串再反序列化属于冗余操作,还容易引发格式异常,convertValue是Jackson提供的内部类型转换工具,适配性更高。 - 自动获取带泛型的类型信息:通过Kotlin反射
prop.returnType.javaType可以直接拿到包含泛型参数的完整类型信息,不需要手动调用Class.forName做反射,自动兼容Kotlin的Set、List等原生集合类型,泛型参数比如Set<Role>中的Role也会被正确识别,不会再出现集合类型不匹配的问题。
可选优化
可以在属性过滤阶段增加逻辑,避免异常场景:
entity::class.memberProperties .filterIsInstance<KMutableProperty<*>>() // 过滤掉标注了@JsonIgnore的属性、以及请求中没有传的字段 .filter { prop -> fields.containsKey(prop.name) && !prop.annotations.any { it is com.fasterxml.jackson.annotation.JsonIgnore } } .forEach { // 原有逻辑 }
内容的提问来源于stack exchange,提问作者Krisztian
相关产品推荐
相关产品推荐

