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的转换:
- 编写自定义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); } } }
- 注册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
相关产品推荐
相关产品推荐

