自定义 LocalDateTimeDeserializer 全局json反序列化处理时区格式转换问题

自定义 LocalDateTimeDeserializer 全局json反序列化处理时区格式转换问题

点击 直接查看结论

背景 :

后端接收国际标准时间格式 yyyy-MM-ddTHH:mm:ss.SSSZ(eg: 2018-01-13T16:00:00.000Z) 的时间格式,要转换成东八区时间,也就是北京时间。我根据经验用如下这种常见的方式处理:

@Configuration
public class CusLocalDateTimeConfig {
     

    static final String PATTERN_UTC = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

    static final String UTC_TAIL = "Z";
    
    @Bean
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
     
        builder.deserializerByType(LocalDateTime.class, CusLocalDateTimeDeserializer.CUS_INSTANCE);
        return builder.createXmlMapper(false).build();
    }

    /**
     * 继承默认的 LocalDateTimeDeserializer 重写反序列方法
     */
    public static class CusLocalDateTimeDeserializer extends LocalDateTimeDeserializer {
     

        public static final CusLocalDateTimeDeserializer CUS_INSTANCE = new CusLocalDateTimeDeserializer(DateTimeFormatter.ofPattern(PATTERN_UTC));

        public CusLocalDateTimeDeserializer(DateTimeFormatter formatter) {
     
            super(formatter);
        }

        @Override
        public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
     
            String timeStr = parser.getText();
            if (timeStr != null && !"".equals(timeStr)) {
     
                if (timeStr.endsWith(UTC_TAIL)) {
     
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN_UTC);
                    return LocalDateTime.parse(timeStr, formatter).plusHours(8);
                }
            }
            return super.deserialize(parser, context);
        }
    }
}

通过Jackson2ObjectMapperBuilder创建一个 ObjectMapper,指定对LocalDateTime的反序列化处理类为我自定义的处理类。

可是,执行了一下,压根没执行我自定义的这个反序列方法。网上搜了一下也没有找到相关问题。

所以决定看看源码找找问题:

因为我对springmvc大体流程还是比较了解,我后台controller是用的 @RequestBody 接收参数,所以参数处理类是RequestResponseBodyMethodProcessor 这个类的这个方法
自定义 LocalDateTimeDeserializer 全局json反序列化处理时区格式转换问题_第1张图片
跟着断点依次执行到这里
自定义 LocalDateTimeDeserializer 全局json反序列化处理时区格式转换问题_第2张图片
自定义 LocalDateTimeDeserializer 全局json反序列化处理时区格式转换问题_第3张图片
经过几次断点调试后发现,这个body就是完成参数处理后的对象,所以处理参数的就是messageConverters中的某一个类

再次断点发现是如下处理类

自定义 LocalDateTimeDeserializer 全局json反序列化处理时区格式转换问题_第4张图片

然后搜了一下,发现在springboot的自动配置类里面

自定义 LocalDateTimeDeserializer 全局json反序列化处理时区格式转换问题_第5张图片

通过ObjectMapper创建一个MappingJackson2HttpMessageConverter类型的bean

所以我之前的做法的完全正确的,但是通过ConditionalOnMissingBean我猜测,也许是项目里面已经定义了这样的Bean我全局搜了一下,果然在某个包中发现了这样一个东西。

ps:此功能主要是因为js的精度问题 序列化Long型为字符串
自定义 LocalDateTimeDeserializer 全局json反序列化处理时区格式转换问题_第6张图片

所以我就把上面的内容加到这个地方,问题就解决了。

当然中途还发现了很多框架中其他的有意思的操作,就不细述

总结 :

LocalDateTime 自定义时区转换的一种解决方式 : 全局反序列化处理 (此处以时间问题为例,当然也可以做其它任意操作)

1、对于参数是 @RequestBody 接收,也就是参数是json格式的场景:

 public void save(@RequestBody Entity param)

第一种情况,项目中没使用自定义MappingJackson2HttpMessageConverter类型Bean的情况,可以直接创建一个ObjectMapper的Bean,指定builder中的反序列化类及对应类型


	@Bean
    public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
     
        builder.deserializerByType(LocalDateTime.class, CusLocalDateTimeDeserializer.CUS_INSTANCE);
        return builder.createXmlMapper(false).build();
    }

    /**
     * 自定义反序列化类
     * 继承默认的 LocalDateTimeDeserializer 重写反序列方法
     */
    public static class CusLocalDateTimeDeserializer extends LocalDateTimeDeserializer {
     

        public static final CusLocalDateTimeDeserializer CUS_INSTANCE = new CusLocalDateTimeDeserializer(DateTimeFormatter.ofPattern(PATTERN_UTC));

        public CusLocalDateTimeDeserializer(DateTimeFormatter formatter) {
     
            super(formatter);
        }

        @Override
        public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
     
            String timeStr = parser.getText();
            if (timeStr != null && !"".equals(timeStr)) {
     
                if (timeStr.endsWith(UTC_TAIL)) {
     
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(PATTERN_UTC);
                    return LocalDateTime.parse(timeStr, formatter).plusHours(8);
                }
            }
            return super.deserialize(parser, context);
        }
    }


第二种情况,自定义MappingJackson2HttpMessageConverter类型的bean,(如果存在多个Bean,先加载的先执行,后加载的就不会再执行。可以通过@Order控制。但是注意千万别随意这样做) 在其他人也定义了Bean的时候,最好还是把功能放在同一个mappingJackson2HttpMessageConverter。以免导致其他功能失效

	//在此之前应该先检查项目中有没有其他人创建MappingJackson2HttpMessageConverter类型的Bean
	@Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
     
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addDeserializer(LocalDateTime.class, CusLocalDateTimeDeserializer.CUS_INSTANCE);
        objectMapper.registerModule(javaTimeModule);
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
        return mappingJackson2HttpMessageConverter;
    }

2、对于参数是普通实体类接收 ,非json格式的场景:

 public void save(Entity param)

可以实现 GenericConverter 或者 ConditionalGenericConverter

	/**
     * 自定义字符串到LocalDateTime的普通参数转换
     *
     **/
    @Component
    public class StringToLocalDateTimeConverter implements GenericConverter {
     

        @Override
        public Set<ConvertiblePair> getConvertibleTypes() {
     
            //将字符串转化为LocalDateTime
            return Collections.singleton(new ConvertiblePair(String.class, LocalDateTime.class));
        }
		//具体转换方法
        @Override
        public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
     
            if (((String) source).endsWith("Z")) {
     
                return stringToLocalDateTimeUtc((String) source);
            }
            return stringToLocalDateTimeGeneral((String) source);
        }
    }

ConditionalGenericConverter ,他多提供了一个是否采用此方法的判断

你可能感兴趣的:(java,java,json,源码)