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

使用@ModelAttribute接收含List字段的DTO时类型转换失败

问题分析

你遇到的报错Failed to convert value of type "java.lang.String" to required type "java.lang.List",核心原因是Spring的@ModelAttribute默认仅支持传统表单键值对格式的参数绑定,而前端通过JSON.stringify([])将children序列化为JSON字符串传递,Spring无法自动将该字符串转换为List<ChildDTO>类型。

以下是三种可行的解决办法:

方法一:后端添加字符串转List的转换器

编写一个Spring Converter,实现将JSON字符串转换为List<ChildDTO>的逻辑,Spring会自动使用该转换器处理children参数:

@Component
public class StringToChildDTOListConverter implements Converter<String, List<ChildDTO>> {
    private final ObjectMapper objectMapper;

    // 注入Spring自带的ObjectMapper实例
    public StringToChildDTOListConverter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public List<ChildDTO> convert(String source) {
        try {
            // 使用TypeReference指定泛型类型,避免类型擦除问题
            return objectMapper.readValue(source, new TypeReference<List<ChildDTO>>() {});
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException("children参数格式错误,需传入合法JSON数组", e);
        }
    }
}

添加该转换器后,前端无需修改代码,Spring会自动完成JSON字符串到List的转换。

方法二:前端调整表单参数格式

放弃JSON序列化,将children数组中的每个对象拆分为带索引的表单字段,Spring的@ModelAttribute原生支持这种索引式参数绑定:

const formData = new FormData();
// 示例子对象数组
const children = [{id: 1, name: "张三"}, {id: 2, name: "李四"}];
children.forEach((child, index) => {
    formData.append(`children[${index}].id`, child.id);
    formData.append(`children[${index}].name`, child.name);
});
// 添加其他字段
// formData.append("otherField", "value");

axios.put("http://localhost:8080/api", formData);

这种方式无需修改后端代码,Spring会自动将参数绑定到MyObjectDTO的children列表中。

方法三:改用@RequestPart分离DTO和文件

如果DTO中包含文件字段,可以将DTO与文件通过@RequestPart分别接收,Spring会使用JSON消息转换器处理DTO,天然支持List类型解析:

后端接口修改:

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

@PutMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
ResponseEntity<MyObject> updateMyObject(
    @RequestPart("myObjectDTO") MyObjectDTO myObjectDTO,
    @RequestPart(value = "file", required = false) MultipartFile file // 按需添加文件参数
) {
    // 业务逻辑处理
    return ResponseEntity.ok(...);
}

前端请求修改:

const formData = new FormData();
// 构造完整DTO对象
const dto = {
    // 其他字段
    children: [{id: 1, name: "张三"}]
};
// 将DTO转换为Blob作为独立表单Part
formData.append('myObjectDTO', new Blob([JSON.stringify(dto)], {type: 'application/json'}));
// 若存在文件则添加
// formData.append('file', yourFile);

axios.put("http://localhost:8080/api", formData, {
    headers: {
        'Content-Type': 'multipart/form-data'
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 14:36:18