如何在Spring Boot中用Jackson将JSON键值转为User列表
问题场景
给定JSON数据:
{ "USER0001": { "name": "hoge", "age": 20 }, "USER0002": { "name": "huga", "age": 10 } }
对应的Kotlin数据类:
data class User( val id: String, val name: String, val age: Int )
期望在Spring控制器中,将上述JSON直接反序列化为List<User>:
listOf( User("USER0001", "hoge", 20), User("USER0002", "huga", 10), )
当前控制器代码:
@RestController class MyController() { fun test(@RequestBody users: List<User>) { // 期望直接使用users列表 } }
尝试自定义反序列化器但遇到问题:
class Deserializer : JsonDeserializer<List<User>>() { override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): List<User> { val treeNode = parser.codec.readTree<TreeNode>(parser) val fieldNames = treeNode.fieldNames() val result = mutableListOf<User>() while(fieldNames.hasNext()) { val fieldName = fieldNames.next() val userJson = treeNode.get(fieldName) // 不知道如何从userJson中提取值并构建User对象 } return result } }
解决方案
方法一:完善自定义JSON反序列化器
可以通过Jackson的API提取节点值,或者用ObjectMapper将节点转换为User对象,以下是完整实现:
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.TreeNode import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JsonDeserializer import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.annotation.JsonDeserialize import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.springframework.boot.jackson.JsonComponent @JsonComponent // 自动注册到Spring Jackson配置 class UserListDeserializer : JsonDeserializer<List<User>>() { override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): List<User> { val treeNode = parser.codec.readTree<TreeNode>(parser) val result = mutableListOf<User>() val objectMapper = jacksonObjectMapper() val fieldNames = treeNode.fieldNames() while (fieldNames.hasNext()) { val userId = fieldNames.next() val userNode = treeNode.get(userId) as JsonNode // 方式1:手动提取字段值构建User val userName = userNode.get("name").asText() val userAge = userNode.get("age").asInt() result.add(User(userId, userName, userAge)) // 方式2:用ObjectMapper转换节点为User,再补充id // val userWithoutId = objectMapper.treeToValue(userNode, User::class.java) // result.add(userWithoutId.copy(id = userId)) } return result } }
之后在控制器参数上指定使用该反序列化器:
@RestController class MyController() { fun test(@RequestBody @JsonDeserialize(using = UserListDeserializer::class) users: List<User>) { // 正常使用users列表 } }
方法二:更简便的方式——接收Map再转List
无需自定义反序列化器,直接接收Map<String, User>后转换为目标列表:
@RestController class MyController() { fun test(@RequestBody userMap: Map<String, User>) { val users = userMap.map { (id, user) -> user.copy(id = id) } // 使用users列表 } }
这种方式利用Jackson默认的Map反序列化能力,通过Kotlin的map函数快速转换,代码简洁易维护。
内容的提问来源于stack exchange,提问作者sho
相关产品推荐
相关产品推荐

