json中的日期格式化@JsonFormat、@DateTimeFormat jackjson,fastjson,gson等

场景

订单日期原来为字符串字段。如2020-06-06
版本升级,变为Date字段。
接口报文显示有了差别:

原来报文 2020-06-06
现在报文 2020-06-06 00:00:00

接口面对客户,尽量不动,所以调整代码来适配。

方案一:
实体字段仍为String,调整sql语句为to_char(createDate,‘yyyy-MM-dd’)。
评估:
数据库方面其实还好,因为函数是加在select的列上,对性能方面没太大影响。
代码改动方面不太友好,因为该字段可能多个mapper调用,所有用到该字段地方都要改。

方案二(推荐):
实体字段修改为Date,添加注解只对这个字段进行格式化。
评估:
实体一般唯一,改动量较小。而且注解代码清爽,易于维护。

解决方案

那么就用注解的方式来实现吧。需要注意的是,格式化json的方式不只一种,例如:jackjson,gson,fastjson等。 所以实现的时候要配对。

jackjson实现格式化

配置类代码:

@JsonComponent
public class DateFormatConfig {

    @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm}")
    private String pattern;


    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilder() {

        return builder -> {
            TimeZone tz = TimeZone.getTimeZone("UTC");
            DateFormat df = new SimpleDateFormat(pattern);
            df.setTimeZone(tz);
            builder.failOnEmptyBeans(false)
                    .failOnUnknownProperties(false)
                    .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                    .dateFormat(df);
        };
    }
    
    @Bean
    public LocalDateTimeSerializer localDateTimeDeserializer() {
        return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));
    }

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeDeserializer());
    }
}

实体代码:

@Data
public class User {
    @NotBlank(message = "这个姓名不能为空")
    private String username;
    private String password;
    @JsonFormat(pattern="yyyy-MM-dd",timezone="GMT+8")
//    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date createDate;
}

fastjson实现序列化

配置类:

@Configuration
public class FastJsonConverterConfig {

    @Bean
    public HttpMessageConverters fastJsonHttpMessageConverters() {
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        fastConverter.setFastJsonConfig(fastJsonConfig);
        HttpMessageConverter<?> converter = fastConverter;
        return new HttpMessageConverters(converter);
    }
}

实体类:

@Data
public class User {
    @NotBlank(message = "这个姓名不能为空")
    private String username;
    private String password;
    @JSONField(format = "yyyy-MM-dd")
    private Date createDate;
}

实测成功。

这种配置,自定义注解无效:

@Configuration
public class FastJsonConverterConfig {

    @Bean
    public HttpMessageConverters fastJsonHttpMessageConverters() {
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.WriteNullBooleanAsFalse,
                SerializerFeature.PrettyFormat  // 不写这行,格式化无效
        );
        fastConverter.setFastJsonConfig(fastJsonConfig);
        //全局指定了日期格式
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm");
        //该设置目的,为了兼容jackson
  		fastConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,MediaType.APPLICATION_JSON_UTF8,MediaType.APPLICATION_OCTET_STREAM));
        HttpMessageConverter<?> converter = fastConverter;
        return new HttpMessageConverters(converter);
    }
}

Gson实现格式化

Gson配置类:

@Configuration
public class GsonConfig {
    @Bean
    public HttpMessageConverters gsonHttpMessageConverters() {
        GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter();
        Gson gson = new GsonBuilder()
                .registerTypeAdapter(Date.class, new MyDateTypeAdapter())
                .serializeNulls()        //null值属性也需要序列化
                .setDateFormat("yyyy-MM-dd HH:mm") //设置日期转换
                .create();
        gsonHttpMessageConverter.setGson(gson);
        HttpMessageConverter<?> converter = gsonHttpMessageConverter;
        return new HttpMessageConverters(converter);
    }
}

自定义转换类:

public class MyDateTypeAdapter extends TypeAdapter<Date> {
    @Override
    public void write(JsonWriter jsonWriter, Date date) throws IOException {
        if(null!=date){
          jsonWriter.value(DateUtils.formatDate(date,DateUtils.YYYY_MM_DD));
        }
    }
    @Override
    public Date read(JsonReader in) throws IOException {
        return null;
    }
}

对象使用类:

@JsonAdapter(value=MyDateTypeAdapter.class)
private Date createDate;

发现了吧Gson是没有直接可以使用的注解的,需要自定义。

小结

spring中HttpMessageConverter负责处理格式化。
默认的json工具是jackjson。
如果想用其他json工具,重写HttpMessageConverter即可。

jackjson,fastjson,gson的格式化方式不同:

种类用法备注
jackjson@JsonFormat(pattern=“yyyy-MM-dd”)-
fastjson@JSONField(format = “yyyy-MM-dd”)-
gson自定义TypeAdapter,然后加到字段上无直接可用的注解

数据库是Date,实体字段是String,用哪个注解呢

用下面代码吗?

@JsonFormat(pattern="yyyy-MM-dd",timezone="GMT+8")
private String createDate;

不对。
因为这个注解都是对日期才有效。
如果字段类型为String的话,那么可以在查询语句中格式化下。

其他

常见的相关注解

要给报文格式化,加上这2个注解即可。

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8") // 返回到前台
@DateTimeFormat(pattern = "yyyy-MM-dd") // 到后台,例如入参报文到后台
private Date createDate;

为什么不用InitBinder呢

controller配置@InitBinder来进行格式化。
评估: 搞清楚,@InitBinder使用来处理前端到后台字段不会字段转换的问题,不应该用在这里,所以pass掉。

@JsonFormat和@DateTimeFormat的区别

区别和相同点如下。

@JsonFormat@DateTimeFormat
归属是jackjson的注释是spring的注释
作用是从后台到前台的格式化从前台到后台的格式化。使用@DateTimeFormat时,前台向后台传数据,必须要使用url拼接参数的方式才生效。(这句未实测,持保留态度)
格式化格式化规则相同。如:@DateTimeFormat(pattern = “yyyy-MM-dd HH:mm:ss”)
@DateTimeFormat(pattern = “yyyy-MM-dd”)
相同

2024-08-17 实际使用和记录的不一样。

按网上搜到的是@DateTimeFormat是前端转后端,那么前端传2024-08-17,后台应该收到啊,但是居然报错400。

@DateTimeFormat(pattern = "yyyy-MM-dd")
//    @JsonFormat(pattern = "yyyy-MM-dd")
private Date updateDate;

但是放开@JsonFormat的注解居然就可以了,和之前知道的正好相反。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值