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

Spring MVC全局替换Jackson StringDeserializer失效问题排查

问题描述

我正在开发Spring MVC项目,需要全局替换Jackson默认的StringDeserializer,对传入的JSON字符串做清理处理。目标是无需给200多个DTO的每个字段添加注解,自动对所有DTO的String字段应用清理逻辑。

我已编写自定义StringDeserializer并在Spring配置中注册,但反序列化方法从未被调用。

自定义反序列化器代码

import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;

public class SanitizeDeserializer extends StringDeserializer {

  @Override
  public String deserialize(
      final JsonParser aParser,
      final DeserializationContext aContext) throws IOException {
    // 这段代码从未执行
    return Jsoup.clean(super.deserialize(aParser, aContext), Safelist.none());
  }    
}

注册反序列化器的Spring配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

@Configuration
public class SanitizeConfiguration {
    
  @Bean
  public Module addSanitizeStringDeserializer() {
    // 这段代码启动时能正常执行
    final SimpleModule module = new SimpleModule();
    module.addDeserializer(String.class, new SanitizeDeserializer());
    return module;
  }
}

REST控制器示例

@PutMapping("/document/{id}")
public ResponseEntity<String> save(@RequestBody DocumentDTO document) {
    return ResponseEntity.ok(document.getId());
}

@Getters
@Setters
public class DocumentDTO {
  // 希望反序列化JSON时,这些String字段在调用setter前先被清理
  private String id;
  private String title;
  private String content;
}

我在deserialize方法中设置了断点,但从未触发,反而进入Jackson原生的StringDeserializer方法。请问是配置错误或遗漏了什么?或者是否无法对已定义类型自定义序列化器?


编辑1:

我尝试配置MessageConverters,但覆盖了默认配置(WebMvcConfigurationSupport.addDefaultHttpMessageConverters),虽然所有String字段都能通过自定义反序列化器处理,但XML内容转换出现异常。

@Override
public void configureMessageConverters(
    final List<HttpMessageConverter<?>> aConverters) {
  final Jackson2ObjectMapperBuilder iBuilder = new Jackson2ObjectMapperBuilder().deserializerByType(String.class,
      new SanitizeDeserializer());
  aConverters.add(new MappingJackson2HttpMessageConverter(iBuilder.build()));
  // 删掉下面这行的话,XML响应无法处理(500错误)
  // 但加上这行,XML响应会被转义,变成无效格式
  aConverters.add(new MappingJackson2XmlHttpMessageConverter(new Jackson2ObjectMapperBuilder().xml().build()));
}

随后我尝试添加Jackson2ObjectMapperBuilder Bean,但仍然未生效:

@Bean
public Jackson2ObjectMapperBuilder configureJackson2ObjectMapperBuilder() {
  final Jackson2ObjectMapperBuilder iBuilder = new Jackson2ObjectMapperBuilder();
  final SimpleModule iSanitizeModule = new SimpleModule("sanitize-module");
  iSanitizeModule.addDeserializer(String.class, new SanitizeDeserializer());
  iBuilder.modules(iSanitizeModule);
  return iBuilder;
}

@Bean
public MappingJackson2HttpMessageConverter configureMappingJackson2HttpMessageConverter() {
  final MappingJackson2HttpMessageConverter iConverter = new MappingJackson2HttpMessageConverter();
  iConverter.setObjectMapper(configureJackson2ObjectMapperBuilder().build());
  return iConverter;
}

解决方案

问题核心在于Spring MVC中Jackson组件的加载优先级,以及自定义配置与默认配置的冲突。以下是两种可行的解决方式:

方式一:标记自定义ObjectMapper为@Primary

当Spring容器中存在多个ObjectMapper实例时,需标记自定义实例为@Primary,确保Spring MVC优先使用它处理JSON序列化/反序列化,且不会影响XML消息转换器(XML使用独立的XmlMapper实例)。

修改配置类如下:

@Configuration
public class SanitizeConfiguration {
    
  @Bean
  @Primary
  public ObjectMapper sanitizedObjectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    SimpleModule module = new SimpleModule();
    module.addDeserializer(String.class, new SanitizeDeserializer());
    objectMapper.registerModule(module);
    return objectMapper;
  }
}

方式二:扩展WebMvcConfigurer修改现有转换器

避免继承WebMvcConfigurationSupport(会覆盖默认配置),转而实现WebMvcConfigurer,通过extendMessageConverters方法修改已有的JSON转换器,保留所有默认消息转换器(包括XML):

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 遍历找到JSON消息转换器,添加自定义反序列化器
        for (HttpMessageConverter<?> converter : converters) {
            if (converter instanceof MappingJackson2HttpMessageConverter) {
                MappingJackson2HttpMessageConverter jacksonConverter = (MappingJackson2HttpMessageConverter) converter;
                SimpleModule module = new SimpleModule();
                module.addDeserializer(String.class, new SanitizeDeserializer());
                jacksonConverter.getObjectMapper().registerModule(module);
                break;
            }
        }
    }
}

之前配置失效的原因

  1. 直接返回Module Bean:Spring会注册模块,但不会自动将其绑定到所有ObjectMapper实例,默认实例可能未加载该模块。
  2. 自定义Jackson2ObjectMapperBuilder未标记@Primary:Spring可能仍使用默认Builder,导致配置不生效。
  3. 使用configureMessageConverters替换所有转换器:丢失默认XML转换器的配置,手动添加的XML转换器未继承默认设置,导致格式异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 11:57:31