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

Spring Web中请求体转异构DTO及特定方法自定义转换器实现

这个场景我之前做项目时碰到过好几次,Spring默认的JSON转换在字段结构不匹配的时候确实有点挠头,不过有几种实用的解决方案,正好能满足你的需求:

解决方案1:用Jackson自定义反序列化器(精准针对特定方法)

因为Spring MVC默认用Jackson处理JSON序列化/反序列化,所以最直接的方式是给目标类写一个自定义反序列化器,并且只在指定的控制器方法中生效,不会影响全局。

步骤如下:

  1. 先定义你的扁平化Person类:
public class Person {
    private String name;
    private String city;

    // 全参构造器、getter/setter省略
}
  1. 编写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);
    }
}
  1. 在控制器方法的参数上指定这个反序列化器,这样就只会对该方法生效:
@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,并控制它的生效范围。

  1. 自定义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);
    }
}
  1. 注册转换器到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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 19:37:49