Spring Web中请求体转异构DTO及特定方法自定义转换器实现
这个场景我之前做项目时碰到过好几次,Spring默认的JSON转换在字段结构不匹配的时候确实有点挠头,不过有几种实用的解决方案,正好能满足你的需求:
解决方案1:用Jackson自定义反序列化器(精准针对特定方法)
因为Spring MVC默认用Jackson处理JSON序列化/反序列化,所以最直接的方式是给目标类写一个自定义反序列化器,并且只在指定的控制器方法中生效,不会影响全局。
步骤如下:
- 先定义你的扁平化
Person类:
public class Person { private String name; private String city; // 全参构造器、getter/setter省略 }
- 编写Jackson自定义反序列化器,负责把嵌套的JSON结构转成
Person:
public class PersonDeserializer extends StdDeserializer<Person> { public PersonDeserializer() { super(Person.class); } @Override public Person deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { JsonNode node = p.getCodec().readTree(p); // 从顶层取name String name = node.get("name").asText(); // 从嵌套的address节点取city String city = node.get("address").get("city").asText(); return new Person(name, city); } }
- 在控制器方法的参数上指定这个反序列化器,这样就只会对该方法生效:
@RestController @RequestMapping("/api") public class PersonController { @PostMapping("/person") public ResponseEntity<Person> createPerson( @RequestBody @JsonDeserialize(using = PersonDeserializer.class) Person person) { // 业务逻辑处理 return ResponseEntity.ok(person); } }
解决方案2:自定义HttpMessageConverter(底层转换,支持全局/局部)
如果你想做更底层的HTTP消息转换,类似自定义HttpConverter的效果,可以扩展Spring的MappingJackson2HttpMessageConverter,并控制它的生效范围。
- 自定义MessageConverter,嵌入刚才的反序列化器:
public class PersonHttpMessageConverter extends MappingJackson2HttpMessageConverter { public PersonHttpMessageConverter() { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(Person.class, new PersonDeserializer()); objectMapper.registerModule(module); setObjectMapper(objectMapper); } // 重写canRead方法,实现细粒度控制(比如只处理Person类型,或特定请求条件) @Override public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) { // 这里可以添加额外条件,比如判断请求头、请求路径等 return type.getTypeName().equals(Person.class.getTypeName()) && super.canRead(type, contextClass, mediaType); } }
- 注册转换器到Spring容器,还可以控制生效范围:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 把自定义转换器放在最前面,优先使用 converters.add(0, new PersonHttpMessageConverter()); } }
如果只想针对某个控制器生效,可以结合@ControllerAdvice(assignableTypes = PersonController.class)来配置,或者在canRead方法里添加请求路径判断。
解决方案3:中间DTO转换(简单易维护)
如果不想写复杂的序列化器或转换器,还有一种更直观的方式:先定义一个和请求JSON结构完全匹配的DTO,再在控制器方法里手动转换为Person类。
比如:
// 和请求JSON结构完全匹配的DTO public class PersonRequestDTO { private String name; private AddressDTO address; // getter/setter public static class AddressDTO { private String city; // getter/setter } }
然后控制器方法里做转换:
@PostMapping("/person") public ResponseEntity<Person> createPerson(@RequestBody PersonRequestDTO dto) { Person person = new Person(); person.setName(dto.getName()); person.setCity(dto.getAddress().getCity()); // 业务逻辑处理 return ResponseEntity.ok(person); }
这种方式虽然多了一个DTO类,但代码直观、维护成本低,适合结构差异不大的场景。
内容的提问来源于stack exchange,提问作者JoshKoe
相关产品推荐
相关产品推荐

