You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot RestController+Jackson:如何将JSONObject数组反序列化为Map<String,String>

解决Jackson将对象数组映射为Map的解析异常问题

你遇到的核心问题是:前端传递的codesList是一个包含type和code字段的对象数组,但你的DTO里用Map<String, String>来接收,Jackson默认不知道怎么把数组结构转成键值对形式的Map,所以抛出了解析异常。下面给你两种实用的解决方案,按需选择:


方案一:自定义Jackson反序列化器(推荐)

这种方式直接在DTO层面完成结构转换,不需要额外的中间类,代码更紧凑。

步骤1:编写自定义反序列化器

创建一个继承自JsonDeserializer的类,实现将对象数组转为Map的逻辑:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class CodesListDeserializer extends JsonDeserializer<Map<String, String>> {

    @Override
    public Map<String, String> deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
        JsonNode arrayNode = jsonParser.getCodec().readTree(jsonParser);
        Map<String, String> codesMap = new HashMap<>();

        // 遍历数组中的每个对象,提取type作为键,code作为值
        if (arrayNode.isArray()) {
            Iterator<JsonNode> elements = arrayNode.elements();
            while (elements.hasNext()) {
                JsonNode itemNode = elements.next();
                String type = itemNode.get("type").asText();
                String code = itemNode.get("code").asText();
                // 可选:处理重复type的情况,比如抛异常或者跳过
                if (codesMap.containsKey(type)) {
                    // throw new IllegalArgumentException("Duplicate code type: " + type);
                }
                codesMap.put(type, code);
            }
        }
        return codesMap;
    }
}

步骤2:在DTO字段上指定反序列化器

修改你的ProductDto,在codesList字段上添加@JsonDeserialize注解,指定刚才写的自定义反序列化器:

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

public class ProductDto {
    private String someField;
    
    @JsonDeserialize(using = CodesListDeserializer.class)
    private Map<String, String> codesList;

    // 别忘了添加Getter和Setter方法
    public String getSomeField() { return someField; }
    public void setSomeField(String someField) { this.someField = someField; }
    public Map<String, String> getCodesList() { return codesList; }
    public void setCodesList(Map<String, String> codesList) { this.codesList = codesList; }
}

这样修改后,Jackson就会自动用你写的逻辑把前端的对象数组转成Map,你的REST端点就能正常接收数据了。


方案二:用中间DTO接收数组后转换

如果你不想写自定义反序列化器,可以先把数组接收为对象列表,再手动转成Map,这种方式更直观,适合快速实现。

步骤1:创建数组元素的DTO类

先定义一个用来接收单个code对象的类:

public class CodeItem {
    private String type;
    private String code;

    // Getter和Setter
    public String getType() { return type; }
    public void setType(String type) { this.type = type; }
    public String getCode() { return code; }
    public void setCode(String code) { this.code = code; }
}

步骤2:修改ProductDto或创建请求专用DTO

可以直接修改原DTO,用列表接收后转换,或者创建一个专门用于接收请求的DTO:

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ProductRequestDto {
    private String someField;
    private List<CodeItem> codesList;

    // 转换方法:把列表转成Map
    public Map<String, String> getCodesAsMap() {
        Map<String, String> codesMap = new HashMap<>();
        if (codesList != null) {
            codesList.forEach(item -> codesMap.put(item.getType(), item.getCode()));
        }
        return codesMap;
    }

    // Getter和Setter
    public String getSomeField() { return someField; }
    public void setSomeField(String someField) { this.someField = someField; }
    public List<CodeItem> getCodesList() { return codesList; }
    public void setCodesList(List<CodeItem> codesList) { this.codesList = codesList; }
}

步骤3:在REST端点中处理转换

修改你的端点方法,接收请求专用DTO,然后转换为业务用的ProductDto:

public ResponseEntity<ProductDto> addProduct(@RequestBody ProductRequestDto requestDto) {
    ProductDto productDto = new ProductDto();
    productDto.setSomeField(requestDto.getSomeField());
    // 调用转换方法得到Map
    productDto.setCodesList(requestDto.getCodesAsMap());

    // 后续业务逻辑处理
    return ResponseEntity.ok(productDto);
}

注意事项

  • 如果前端传递的数组中存在重复的type值,两种方案中后面的code都会覆盖前面的,如果你需要处理这种情况(比如抛异常、收集所有值),可以在转换逻辑中添加对应的判断。
  • 确保你的Spring Boot项目中已经引入了Jackson的依赖(Spring Boot Web starter默认包含)。

内容的提问来源于stack exchange,提问作者Alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:06:08