SpringBoot 全局时间格式

申明:本人一般不写博客,苦于此问题资料太零散。

故事从 jackson 的 @JsonFormat 和 spring format 下的

@DateTimeFormat 说起

@JsonFormat :响应数据生效 (实测接收body参数也生效)

@DateTimeFormat:接收参数生效(测试GET参数的确生效)

问题引入:

全局配置时间格式,无论输出还是输入,全局高精度Number转字符串

码代码:

json全局序列化和反序列化配置:

自定义实现Jackson2ObjectMapperBuilderCustomizer或者注入自定义的ObjectMapper

然而,响应生效,接收 body参数也生效,get参数不生效. emm...,难道是哪里写漏了?比如说WebMvcConfigurer.MappingJackson2HttpMessageConverter.setObjectMapper ?需要重写HttpMessageConverter么?

继续捯饬:一通操作猛如虎,结果毫无作用。

好吧,根据报错,进入断点模式:持续跟踪,发现 从WebDataBinder 慢慢直接就走入了org.springframework.core.convert.support.ConversionUtils并没有进入jackson包;抛去各种非全局的解决办法,最后自定义Converter解决问题。

代码如下:

JacksonConfig.java

package com.mrcode.case.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author mrcode@126.com
 * @date 2022/4/2 16:24
 */
@Configuration
@ConditionalOnClass(ObjectMapper.class)
@AutoConfigureBefore(JacksonAutoConfiguration.class)
@Slf4j
public class JacksonConfig {
    final String FORMATTER_DATE_TIME_H = "yyyy-MM-dd HH:mm:ss";
    final String FORMATTER_DATE_TIME_S = "yyyy/MM/dd HH:mm:ss";
    final String FORMATTER_DATE_H = "yyyy-MM-dd";
    final String FORMATTER_DATE_S = "yyyy/MM/dd";
    final String FORMATTER_TIME = "HH:mm:ss";
    final String H = "-";
    final String S = "/";
    final String P = ".";
    final String ZERO_INT64 = "0";

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public Jackson2ObjectMapperBuilderCustomizer customJacksonBuilder() {
        return builder -> {
            /**
             * 配置Date的格式
             */
            builder.locale(Locale.CHINA);
            builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
            builder.simpleDateFormat(FORMATTER_DATE_TIME_H);
            builder.deserializerByType(Date.class, new DateDeserializer());
            /**
             * 配置java8时间格式
             */
            builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(FORMATTER_DATE_TIME_H)));
            builder.serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(FORMATTER_DATE_H)));
            builder.serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(FORMATTER_TIME)));

            builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer());
            builder.deserializerByType(LocalDate.class, new LocalDateDeserializer());
            builder.deserializerByType(LocalTime.class, new LocalTimeDeserializer());
            /**
             * 配置高精度数字字符串格式
             */
            builder.serializerByType(Long.TYPE, ToStringSerializer.instance);
            builder.serializerByType(Long.class, ToStringSerializer.instance);
            builder.serializerByType(BigInteger.class, ToStringSerializer.instance);
            builder.serializerByType(BigDecimal.class, ToStringSerializer.instance);
        };
    }

    @Bean
    @Primary
    public ObjectMapper customObjectMapper(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper objectMapper = builder.createXmlMapper(false).build();
        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                String fieldName = jsonGenerator.getOutputContext().getCurrentName();
                try {
                    Field field = jsonGenerator.getCurrentValue().getClass().getDeclaredField(fieldName);
                    if (Long.class.isAssignableFrom(field.getType())) {
                        jsonGenerator.writeString(ZERO_INT64);
                    } else if (BigInteger.class.isAssignableFrom(field.getType())) {
                        jsonGenerator.writeString(ZERO_INT64);
                    } else if (BigDecimal.class.isAssignableFrom(field.getType())) {
                        jsonGenerator.writeString(ZERO_INT64);
                    } else if (CharSequence.class.isAssignableFrom(field.getType())) {
                        jsonGenerator.writeString(ZERO_INT64);
                    } else if (Collection.class.isAssignableFrom(field.getType())) {
                        jsonGenerator.writeStartArray();
                        jsonGenerator.writeEndArray();
                    } else if (Map.class.isAssignableFrom(field.getType())) {
                        jsonGenerator.writeStartObject();
                        jsonGenerator.writeEndObject();
                    }
                } catch (NoSuchFieldException ignored) {
                    log.error("", ignored);
                }
            }
        });
        /**
         * 不序列化Null值字段
         */
        builder.serializationInclusion(JsonInclude.Include.NON_NULL);
        /**
         * 忽略序列化空对象错误
         */
        objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        /**
         * 忽略反序列化未知属性
         */
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        return objectMapper;
    }

    public class DateDeserializer extends JsonDeserializer<Date> {
        @Override
        public Date deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException {
            String value = p.getValueAsString();
            SimpleDateFormat formatter = new SimpleDateFormat();
            if (value.contains(H)) {
                formatter = new SimpleDateFormat(FORMATTER_DATE_TIME_H);
            } else if (value.contains(S)) {
                formatter = new SimpleDateFormat(FORMATTER_DATE_TIME_S);
            }
            try {
                return formatter.parse(value);
            } catch (ParseException e) {
                return deserializationContext.parseDate(value);
            }
        }
    }

    public class LocalTimeDeserializer extends JsonDeserializer<LocalTime> {
        @Override
        public LocalTime deserialize(JsonParser p, DeserializationContext deserializationContext)
                throws IOException {
            String value = p.getValueAsString();
            if (value.contains(P)) {
                return LocalTime.parse(value);
            } else {
                return LocalTime.parse(value, DateTimeFormatter.ofPattern(FORMATTER_TIME));
            }
        }
    }

    public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext deserializationContext)
                throws IOException {
            String value = p.getValueAsString();
            if (value.contains(H)) {
                return LocalDate.parse(value, DateTimeFormatter.ofPattern(FORMATTER_DATE_H));
            } else if (value.contains(S)) {
                return LocalDate.parse(value, DateTimeFormatter.ofPattern(FORMATTER_DATE_S));
            } else {
                return LocalDate.parse(value);
            }
        }
    }

    public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext)
                throws IOException {
            String value = p.getValueAsString();
            if (value.contains(H)) {
                return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(FORMATTER_DATE_TIME_H));
            } else if (value.contains(S)) {
                return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(FORMATTER_DATE_TIME_S));
            } else {
                return LocalDateTime.parse(value);
            }
        }
    }
}

WebMvcConfig.java

package com.mrcode.case.config;


import com.fasterxml.jackson.databind.util.ClassUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * @author mrcode@126.com
 * @date 2022/4/2 16:24
 */
@Slf4j
@AutoConfigureAfter(JacksonConfig.class)
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    final String FORMATTER_DATE_TIME_H = "yyyy-MM-dd HH:mm:ss";
    final String FORMATTER_DATE_TIME_S = "yyyy/MM/dd HH:mm:ss";
    final String FORMATTER_DATE_H = "yyyy-MM-dd";
    final String FORMATTER_DATE_S = "yyyy/MM/dd";
    final String FORMATTER_TIME = "HH:mm:ss";
    final String H = "-";
    final String S = "/";
    final String P = ".";

    /**
     * 非Jackson转换器
     *
     * @param registry
     */
    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToDateConverter());
        registry.addConverter(new StringToLocalTimeConverter());
        registry.addConverter(new StringToLocalDateConverter());
        registry.addConverter(new StringToLocalDateTimeConverter());
    }

    class StringToDateConverter implements Converter<String, Date> {
        @Override
        public Date convert(String source) {
            SimpleDateFormat formatter;
            if (source.contains(H)) {
                formatter = new SimpleDateFormat(FORMATTER_DATE_TIME_H);
            } else if (source.contains(S)) {
                formatter = new SimpleDateFormat(FORMATTER_DATE_TIME_S);
            } else {
                formatter = new SimpleDateFormat();
            }
            try {
                return formatter.parse(source);
            } catch (ParseException e) {
                throw new IllegalArgumentException(String.format(
                        "Failed to parse Date value '%s': %s", source,
                        ClassUtil.exceptionMessage(e)));
            }
        }
    }

    class StringToLocalTimeConverter implements Converter<String, LocalTime> {
        @Override
        public LocalTime convert(String source) {
            if (source.contains(P)) {
                return LocalTime.parse(source);
            } else {
                return LocalTime.parse(source, DateTimeFormatter.ofPattern(FORMATTER_TIME));
            }
        }
    }

    class StringToLocalDateConverter implements Converter<String, LocalDate> {
        @Override
        public LocalDate convert(String source) {
            if (source.contains(H)) {
                return LocalDate.parse(source, DateTimeFormatter.ofPattern(FORMATTER_DATE_H));
            } else if (source.contains(S)) {
                return LocalDate.parse(source, DateTimeFormatter.ofPattern(FORMATTER_DATE_S));
            } else {
                return LocalDate.parse(source);
            }
        }
    }

    class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {
        @Override
        public LocalDateTime convert(String source) {
            if (source.contains(H)) {
                return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(FORMATTER_DATE_TIME_H));
            } else if (source.contains(S)) {
                return LocalDateTime.parse(source, DateTimeFormatter.ofPattern(FORMATTER_DATE_TIME_S));
            } else {
                return LocalDateTime.parse(source);
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot中,全局配置时间格式可以通过配置文件或代码方式来实现。如果全局配置时间格式无效,有以下几种可能的原因和解决方法: 1. 配置文件错误:首先需要确认配置文件是否正确,并且在正确的位置。在application.properties或application.yml文件中,可以使用以下配置来设置时间格式时间格式为yyyy-MM-dd HH:mm:ss: - 配置文件中:spring.jackson.date-format=yyyy-MM-dd HH:mm:ss - 代码中:@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 时间格式为自定义格式: - 配置文件中:spring.jackson.date-format=自定义格式 - 代码中:@JsonFormat(pattern = "自定义格式") 2. 代码中覆盖配置文件值:在代码中手动指定时间格式,可能会覆盖配置文件中的值。在代码中,可以使用注解@JsonFormat(pattern = "时间格式")来设置时间格式。需要确认代码中是否存在这样的设置。 3. 全局配置不生效:如果以上方法都没有生效,可以尝试在代码中自定义全局时间格式。在Spring Boot中,可以创建一个配置类,使用注解@Configuration来标识该类。在配置类中,通过注解@Bean来自定义时间格式并设置给ObjectMapper,代码示例如下: ``` @Configuration public class MyConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); // 其他配置... return objectMapper; } } ``` 需要注意的是,如果使用了自定义的ObjectMapper配置,会覆盖默认的配置。 综上所述,如果全局配置时间格式无效,可以通过检查配置文件、代码中的设置和自定义配置类来解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值