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

Spring中@ModelAttribute无法转换包装DTO内Map对象的问题咨询

解决@ModelAttribute处理Java Map对象的问题

背景

开发了一个接收包装DTO的POST接口,相关代码如下:

Controller代码

@Validated
@RestController
@Slf4j
@RequestMapping(path = "/api/v1/modelattribute", produces = MediaType.APPLICATION_JSON_VALUE)
public class AController {

  @PostMapping(value = "wrapperdto", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
  public void testMapInWrapperDto(@ModelAttribute("wrapper") WrapperDto wrapper) {
    log.warn(wrapper.toString());
  }
}

WrapperDto代码

@NoArgsConstructor
@AllArgsConstructor
@Data
public class WrapperDto {
  private String number;
  private Map<String, String> map = new HashMap<>();
}

问题

通过Swagger UI或curl调用接口时,Spring返回400 BAD_REQUEST,提示无法将String类型转换为Map,无匹配的编辑器或转换策略。执行的curl命令如下:

curl -X 'POST' \
  'http://localhost:8080/api/v1/modelattribute/wrapperdto' \
  -H 'accept: */*' \
  -H 'Content-Type: multipart/form-data' \
  -F 'number=65431' \
  -F 'map={
  "cheese-burger": "280g",
  "classic-burger": "50g"
}'

解决方案

方法1:使用表单参数格式传递Map

Spring的@ModelAttribute默认支持前缀+键名的参数格式解析Map,无需传入JSON字符串。调整curl命令为:

curl -X 'POST' \
  'http://localhost:8080/api/v1/modelattribute/wrapperdto' \
  -H 'accept: */*' \
  -H 'Content-Type: multipart/form-data' \
  -F 'number=65431' \
  -F 'map[cheese-burger]=280g' \
  -F 'map[classic-burger]=50g'

这种格式下,Spring会自动将map[xxx]格式的参数解析到WrapperDto的map属性中。

方法2:自定义Converter实现JSON转Map

如果必须传入JSON格式字符串给map参数,可自定义Converter让Spring支持JSON到Map的转换:

  1. 编写自定义Converter:
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import java.util.Map;

@Component
public class StringToMapConverter implements Converter<String, Map<String, String>> {

    private final ObjectMapper objectMapper;

    public StringToMapConverter(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public Map<String, String> convert(String source) {
        try {
            return objectMapper.readValue(source, new TypeReference<Map<String, String>>() {});
        } catch (Exception e) {
            throw new IllegalArgumentException("Invalid JSON format for map", e);
        }
    }
}
  1. 注册Converter到Spring MVC配置:
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final StringToMapConverter stringToMapConverter;

    public WebConfig(StringToMapConverter stringToMapConverter) {
        this.stringToMapConverter = stringToMapConverter;
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(stringToMapConverter);
    }
}

配置完成后,原curl命令即可正常解析map参数,Swagger UI传入JSON字符串也能被正确转换。

方法3:改用@RequestBody(非multipart场景适用)

如果接口不需要上传文件,建议改用@RequestBody接收JSON请求体,这种方式对Map的支持更自然:

调整Controller方法:

@PostMapping(value = "wrapperdto", consumes = MediaType.APPLICATION_JSON_VALUE)
public void testMapInWrapperDto(@Valid @RequestBody WrapperDto wrapper) {
    log.warn(wrapper.toString());
}

对应的curl命令改为:

curl -X 'POST' \
  'http://localhost:8080/api/v1/modelattribute/wrapperdto' \
  -H 'accept: */*' \
  -H 'Content-Type: application/json' \
  -d '{
  "number": "65431",
  "map": {
    "cheese-burger": "280g",
    "classic-burger": "50g"
  }
}'

注意:该方式不支持multipart/form-data,若需同时上传文件和传递Map,优先选用前两种方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 12:30:27