网络传输时需要将对象转换为字节流,这时就需要把对象进行序列化;在接收时,需要进行反序列化,把字节流转换为对象。
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。易于人阅读和编写,同时也易于机器解析和生成。
Java中可方便地通过JackSON(参见:《Jackson使用简介与示例》)进行序列化与反序列化。但是因时间格式的复杂与多样性,经常会遇到序列化出错的情况,这时就需要进行特殊处理。
参数中时间处理
以参数方式传递的时间,
public ResponseResult listTest(@RequestParam LocalDateTime dtStart, @RequestParam(required = false) LocalDateTime dtEnd)
{ }
时间字符串作为普通请求参数传入时,转换用的是Converter;此时需要通过Configure来注入Bean,以定制时间的格式化方式:
@Configuration
public class JsonConfig {
static final String DateUndefined = "undefined";
@Bean
public Converter<String, LocalDate> LocalDateConvert() {
return new Converter<String, LocalDate>() {
@Override
public LocalDate convert(String source) {
if (source == null || source.length() == 0)
return null;
// return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
try {
return LocalDate.parse(source, DateTimeFormatter.ISO_LOCAL_DATE);
} catch (DateTimeParseException ex) {
if (!source.equals(DateUndefined)) {
return null;
} else {
throw ex;
}
}
}
};
}
@Bean
public Converter<String, LocalDateTime> LocalDateTimeConvert() {
return new Converter<String, LocalDateTime>() {
@Override
public LocalDateTime convert(String source) {
if (source == null || source.length() == 0)
return null;
try {
return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
} catch (DateTimeParseException ex) {
if (source.equals(DateUndefined)) {
return null;
}
}
try {
return LocalDateTime.parse(source, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
} catch (DateTimeParseException ex) {
throw ex;
}
}
};
}
}
body中时间处理
Body中的时间是使用的Jackson等Json序列化器完成的。
注解方式@JsonFormat
通过@JsonFormat注解,可以设定时间的格式化方式:
@Data
public class TestData {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime logTime;
}
这种方式需要针对每个实体进行设定,且只能是一种固定的格式,复杂且不灵活。
修改Json序列化器
默认情况下,时间是通过LocalDateTimeDeserializer进行反序列化的,若要定制在需要对其进行扩展。
扩展序列化器
只需继承LocalDateTimeDeserializer,对其进行扩展即可:
public class WebLocalDateTimeDeserializer extends LocalDateTimeDeserializer {
static final String DateUndefined = "undefined";
public WebLocalDateTimeDeserializer(DateTimeFormatter formatter){
super(formatter);
}
@Override
public LocalDateTime deserialize(JsonParser parser, DeserializationContext context) throws IOException {
if (parser.hasTokenId(JsonTokenId.ID_STRING)) {
String string = parser.getText().trim();
if (string.length() == 0 || DateUndefined.equals(string)) {
return null;
}
}
return super.deserialize(parser, context);
}
}
对于特殊情形,只需在if部分处理即可,最后再调用父类的deserialize对标准格式的时间进行反序列化。
添加序列化器
要使定制生效,需要把序列化器添加到转换器列表中:
@Configuration
public class WebLocalDateConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
// 这里添加的是自定义的反序列化器
module.addDeserializer(LocalDateTime.class,
new WebLocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
mapper.registerModule(module);
// add converter at the very front
// if there are same type mappers in converters, setting in first mapper is used.
int i=0;
for(HttpMessageConverter con : converters){
if(con instanceof MappingJackson2HttpMessageConverter){
break;
}
++i;
}
converters.add(i, new MappingJackson2HttpMessageConverter(mapper));
}
}
在序列化时,会优先使用最先查找到的序列化器;为使我们的序列化器生效,必须保证其加载到其他同类型序列化器前面;但是不能放在最前面,否则可能会影响其他类型字符串的序列化。