解决全局的jackson日期转换和解析

博客围绕解决全局的Jackson日期转换和解析展开。起初使用@JsonFormat注解,出现格式不兼容问题。尝试在objectMapper中设置全局DateFormat效果不佳。后发现不加Format可解析多种日期格式,还找到了解析代码所在的STDDateformat,最终锁定DeserializationContext类来解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

解决全局的jackson日期转换和解析

因为项目有些特殊,需要解析各种格式的日期类型转换,后来发现jackson在日期转换上没法满足需要。
遂重写了com.fasterxml.jackson.databind.util.StdDateFormat 
和com.fasterxml.jackson.databind.DeserializationContext。
我的版本是jackson-databind-2.8.10

如图所示
起初因为需要“yyyy-MM-dd HH:mm:ss”的日期格式直接显示,直接使用了@JsonFormat注解,设置“yyyy-MM-dd HH:mm:ss”,时区加八个小时。
但是被注解后,不管是解析还是json串,都一律是这个格式。只要不是这个格式那就是六亲不认!比如别人的api中接收我的格式时报错“Can not parse date “xx”: not compatible with any of standard forms (xxxx,xxxx)”,我的格式接收别人的api报错 “Failed to parse Date value ‘xx’: yyyy-MM-dd HH:mm:ss”。
于是我尝试在objectMapper中设置全局的DateFormat:

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/");
    }
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();

        //json中多余的参数不报错,不想要可以改掉
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        //设置全局的时间转化
        SimpleDateFormat smt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        objectMapper.setDateFormat(smt);
        objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));//解决时区差8小时问题


        //设置中文编码格式
        List<MediaType> list = new ArrayList<MediaType>();
        list.add(MediaType.APPLICATION_JSON_UTF8);
        jackson2HttpMessageConverter.setSupportedMediaTypes(list);


        //生成json时,将所有Long转换成String
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);

        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
        converters.add(0, jackson2HttpMessageConverter);
    }
}

没错,只有三句话:
- SimpleDateFormat smt = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
- objectMapper.setDateFormat(smt);
- objectMapper.setTimeZone(TimeZone.getTimeZone(“GMT+8”));
剩下的全是废话。
这样做只能起到微弱的卵用,仅仅只是让每一个date都带有JsonFormat的效果而已,于解析而言,没有任何益处。
后经过我的不断试验,发现如果我不加任何Format的话,默认可以解析yyyy-MM-dd’T’HH:mm:ss.SSSZ、时间戳、EEE, dd MMM yyyy HH:mm:ss zzz等等一系列惨不忍睹的日期格式,经过我的不断寻找终于找到了,解析的代码所在。就是这个STDDateformat。这里我一共写了几项:

/**
     * TODO 自己加入,想加多少加多少
     */
    protected final static String DATE_FORMAT_STR_FIRST= "yyyy-MM-dd HH:mm:ss";
    protected final static String DATE_FORMAT_STR_SECOND= "EEE MMM dd hh:mm:ss z yyyy";`
/**
     * For error messages we'll also need a list of all formats.
     */
    protected final static String[] ALL_FORMATS = new String[] {
        DATE_FORMAT_STR_ISO8601,
        DATE_FORMAT_STR_ISO8601_Z,
        DATE_FORMAT_STR_ISO8601_NO_TZ,
        DATE_FORMAT_STR_RFC1123,
        DATE_FORMAT_STR_PLAIN,
            DATE_FORMAT_STR_FIRST,//把刚写的两个转换格式加入
            DATE_FORMAT_STR_SECOND
    };
        protected final static DateFormat DATE_FORMAT_FIRST;//也是同理,照葫芦画瓢
        protected final static DateFormat DATE_FORMAT_SECOND;
        DATE_FORMAT_FIRST = new SimpleDateFormat(DATE_FORMAT_STR_FIRST, DEFAULT_LOCALE);
        DATE_FORMAT_FIRST.setTimeZone(DEFAULT_TIMEZONE);
        DATE_FORMAT_SECOND = new SimpleDateFormat(DATE_FORMAT_STR_SECOND, DEFAULT_LOCALE);
        DATE_FORMAT_SECOND.setTimeZone(DEFAULT_TIMEZONE);
 @Override
    public Date parse(String dateStr) throws ParseException
    {
        dateStr = dateStr.trim();
        ParsePosition pos = new ParsePosition(0);

        Date dt;

        if (looksLikeISO8601(dateStr)) { // also includes "plain"
            dt = parseAsISO8601(dateStr, pos, true);
        } else {
            // Also consider "stringified" simple time stamp
            int i = dateStr.length();
            while (--i >= 0) {
                char ch = dateStr.charAt(i);
                if (ch < '0' || ch > '9') {
                    // 07-Aug-2013, tatu: And [databind#267] points out that negative numbers should also work
                    if (i > 0 || ch != '-') {
                        break;
                    }
                }
            }
            if ((i < 0)
                // let's just assume negative numbers are fine (can't be RFC-1123 anyway); check length for positive
                    && (dateStr.charAt(0) == '-' || NumberInput.inLongRange(dateStr, false))) {
                dt = new Date(Long.parseLong(dateStr));
            } else {
                // Otherwise, fall back to using RFC 1123
                dt = parseAsRFC1123(dateStr, pos);
            }
        }
        if (dt != null) {
            return dt;
        }
        /////////////////////////////////
        //这里加入解析是非常重要的一步,这个类有两个parse()方法,写在一个参数的那个parse()方法里面,
        //parseDate()建议大家直接引入别人的jar包里封装好的方法,我这里自己写是因为EEE MMM dd hh:mm:ss z yyyy
        //的格式必须要设置正确的locale才可以解析
            String [] s={DATE_FORMAT_STR_SECOND,DATE_FORMAT_STR_FIRST};
            dt = parseDate(dateStr, s,_locale);
            if (dt != null) {
                return dt;
            }
        /////////////////////////////////

        StringBuilder sb = new StringBuilder();
        for (String f : ALL_FORMATS) {
            if (sb.length() > 0) {
                sb.append("\", \"");
            } else {
                sb.append('"');
            }
            sb.append(f);
        }
        sb.append('"');
        throw new ParseException
            (String.format("Can not parse date \"%s\": not compatible with any of standard forms (%s)",
                           dateStr, sb.toString()), pos.getErrorIndex());
    }
//虽然不是必要的,但还是将这个方法贴出来
private static Date parseDate(String str, String[] parsePatterns, Locale _locale)
            throws ParseException
    {
        if ((str == null) || (parsePatterns == null)) {
            throw new IllegalArgumentException("Date and Patterns must not be null");
        }

        SimpleDateFormat parser = null;
        ParsePosition pos = new ParsePosition(0);
        for (int i = 0; i < parsePatterns.length; i++) {
            if (i == 0)
                parser = new SimpleDateFormat(parsePatterns[0],_locale);
            else {
                parser.applyPattern(parsePatterns[i]);
            }
            pos.setIndex(0);
            Date date = parser.parse(str, pos);
            if ((date != null) && (pos.getIndex() == str.length())) {
                return date;
            }
        }
        throw new ParseException("Unable to parse the date: " + str, -1);
    }

这个时候发现不对劲

加入全局格式转换后
Failed to parse Date value ‘xx’: yyyy-MM-dd HH:mm:ss
明显和
throw new ParseException (String.format(“Can not parse date \”%s\”: not compatible with any of standard forms (%s)”,
dateStr, sb.toString()), pos.getErrorIndex());
报的错不一样!,
不加类型转换,报下面这个。加了就调皮。
鉴于我人傻头铁时间多,接着找。找到了DeserializationContext。这个类
他的报错是

throw new IllegalArgumentException(String.format(
                    "Failed to parse Date value '%s': %s", dateStr, e.getMessage()));

这么看来应该是他没跑儿了

//修改parseDate
 public Date parseDate(String dateStr) throws IllegalArgumentException
    {
        try {
            DateFormat df = getDateFormat();
            return df.parse(dateStr);
        } catch (ParseException e) {
        //////////////////////////////////////////
            try {//这里加入了StdDateFormat的parse
                return new StdDateFormat().parse(dateStr);
            } catch (ParseException e1) {
                throw new IllegalArgumentException(String.format(
                    "Failed to parse Date value '%s': %s", dateStr, e.getMessage()));
            }
        //////////////////////////////////////////
        }
    }

虽然做法有些粗暴,一时半会儿也没什么其他办法,算是能用吧。

<think>好的,我现在需要帮助用户解决如何将全局Jackson转换器配置到WebConfig中的问题。用户之前遇到了MethodArgumentNotValidException,问题出在日期格式转换上,现在他们想知道如何直接在WebConfig里配置Jackson。 首先,我需要回忆一下Spring MVC中如何配置消息转换器。通常,这是通过扩展WebMvcConfigurer并重写configureMessageConverters方法或者扩展其中的MappingJackson2HttpMessageConverter来实现的。用户可能已经在实体类上使用了@JsonFormat注解,但可能需要全局配置来确保所有日期都按照特定格式处理,而不仅仅是某个字段。 接下来,用户提到要配置到WebConfig中,这意味着他们可能有一个配置类,比如WebConfig或者WebMvcConfig,用来自定义Spring的Web相关设置。我需要给出具体的代码示例,展示如何在这个配置类中创建ObjectMapper并注册相应的模块,比如JavaTimeModule,同时禁用日期作为时间戳的序列化。 还需要考虑依赖的问题。用户是否正确引入了Jackson的依赖,特别是处理Java 8日期时间的模块,比如jackson-datatype-jsr310。如果用户使用的是较旧的Spring版本,可能需要手动添加这些依赖,而Spring Boot可能会自动配置,但有时候需要显式配置。 另外,用户可能需要了解时区设置的重要性,确保服务器客户端之间的时区一致,比如使用UTC。在配置ObjectMapper时,设置时区可以避免不必要的转换错误。 还要注意,配置ObjectMapper的方式有多种,比如使用@Bean或者在configureMessageConverters中直接修改现有的转换器。需要确保配置生效,可能涉及到覆盖默认的转换器或者调整优先级。 最后,可能需要提醒用户进行测试,比如发送包含日期字段的请求,观察日志输出,确认日期是否正确解析,或者使用Postman等工具验证请求体的格式是否符合预期。</think>以下是直接在Spring Web配置类(通常命名为`WebConfig`或`WebMvcConfig`)中配置全局Jackson转换器的完整方案: --- ### **配置步骤详解** #### **1. 创建Web配置类** ```java @Configuration @EnableWebMvc public class WebConfig implements WebMvcConfigurer { // 核心配置方法 @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { // 优先清除默认转换器(可选) converters.removeIf(c -> c instanceof MappingJackson2HttpMessageConverter); // 添加自定义配置的转换器 converters.add(mappingJackson2HttpMessageConverter()); } // 创建自定义消息转换器 @Bean public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setObjectMapper(objectMapper()); return converter; } // 配置全局ObjectMapper @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); // 注册Java8时间模块 mapper.registerModule(new JavaTimeModule()); // 禁用日期时间戳 mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 设置日期格式(可覆盖@JsonFormat的局部配置) mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX")); // 设置时区(关键配置) mapper.setTimeZone(TimeZone.getTimeZone("UTC")); return mapper; } } ``` --- ### **关键配置说明** #### **1. 模块注册** - `JavaTimeModule`:必须注册以支持`java.time.*``java.util.Date`的序列化/反序列化 - 需要确保pom.xml中有依赖: ```xml <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.13.3</version> </dependency> ``` #### **2. 时区处理** - `mapper.setTimeZone("UTC")`:统一服务端与客户端的时区基准 - 如果前端使用本地时区,需调整为: ```java mapper.setTimeZone(TimeZone.getDefault()); ``` #### **3. 格式优先级** - 全局格式:`yyyy-MM-dd'T'HH:mm:ss.SSSXXX`(匹配ISO 8601带毫秒格式) - 局部注解优先:若字段有`@JsonFormat`注解,会覆盖全局配置 --- ### **验证配置是否生效** #### **1. 测试Controller** ```java @PostMapping("/test") public ResponseEntity<?> testDate(@RequestBody Product product) { System.out.println("Received date: " + product.getCreatedTime()); return ResponseEntity.ok(product); } ``` #### **2. 使用Postman发送请求** ```json { "createdTime": "2025-03-06T15:23:49.123Z", "updatedTime": "2025-03-06T15:23:49.456Z" } ``` #### **3. 预期输出** ``` CreatedTime: Thu Mar 06 15:23:49 UTC 2025 ``` --- ### **常见问题排查** #### **1. 配置未生效检查** - 确保配置类有`@Configuration``@EnableWebMvc`注解 - 检查是否多个配置类冲突 - 查看启动日志中注册的`HttpMessageConverter` #### **2. 依赖冲突检测** ```bash mvn dependency:tree | grep jackson ``` 确保版本统一(推荐使用Spring Boot管理的Jackson版本) #### **3. 时区测试技巧** 在配置类中添加: ```java @PostConstruct void checkTimeZone() { System.out.println("Jackson TimeZone: " + objectMapper().getTimeZone()); } ``` --- ### **高级配置(可选)** #### **1. 自定义反序列化器** ```java public class CustomDateDeserializer extends JsonDeserializer<Date> { @Override public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { // 自定义解析逻辑 } } // 注册到模块 SimpleModule module = new SimpleModule(); module.addDeserializer(Date.class, new CustomDateDeserializer()); mapper.registerModule(module); ``` #### **2. 处理空值策略** ```java mapper.setSerializationInclusion(Include.NON_NULL); ``` 通过以上配置,可以确保整个Spring应用的JSON序列化/反序列化过程统一处理日期格式。如果仍有问题,建议在`objectMapper()`方法中添加调试日志输出实际配置值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值