文章目录
场景
订单日期原来为字符串字段。如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
的注解居然就可以了,和之前知道的正好相反。
Fri Oct 11 10:55:27 CST 2024 这样格式字符串如何映射呢?
报错信息:
Can not deserialize value of type java.util.Date from String “Sat Oct 12 09:53:18 CST 2024”: not a valid representation (error: Failed to parse Date value ‘Sat Oct 12 09:53:18 CST 2024’: Can not parse date “Sat Oct 12 09:53:18 CST 2024”: not compatible with any of standard forms (“yyyy-MM-dd’T’HH:mm:ss.SSSZ”, “yyyy-MM-dd’T’HH:mm:ss.SSS’Z’”, “EEE, dd MMM yyyy HH:mm:ss zzz”, “yyyy-MM-dd”))
比较奇特,直接Date是接不过来的。
如下格式也接不过来。
@DateTimeFormat(pattern = “yyyy-MM-dd HH:mm:ss”)
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”,timezone=“GMT+8”)
这个格式也接不过来,那么如何才能接过来呢? // TODO
(“EEE MMM dd HH:mm:ss zzz yyyy”)