写在前面


正文

需求

由于第三方接口提供方,在给我返回JSON串数据的时候,把空的时间,按照”00000000”的内容返回了,如果直接序列化的话,会被直接转为计算机起始时间“1970-01-01”实在是坑,但是客户就是上帝,没有办法!

解决方案

找了很多的博客,参考其中一个,进行了定制化修改,初步想到两种处理方式。

  • 改造set方法,在涉及到相关的Date格式的字段,在set方法中添加判断。
  • 往上想一步,肯定在反序列化的时候,是存在自定义反序列化的。

0.ObjectMapper反序列化JSON对象

使用ObjectMapper来进行反序列化,同样可以采用JSON.parseObject()来进行,就不需要设置(yyyyMMdd)时间格式。

1
2
3
4
5
ObjectMapper objectMapper = new ObjectMapper();
//设置时间格式
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
objectMapper.setDateFormat(dateFormat);
response = objectMapper.readValue(result, VoucherResponseDto.class);

1. @JsonDeserialize的使用

1
2
3
@JsonProperty("DATE")
@JsonDeserialize(using = "DateDerializerCustom"
private Date date;

本质上,这个注解,是在属性的setter方法上起作用,把Json串对应属性名的字符串值转换为该属性的值,该注解常用的参数就是using,也就是使用哪个反编译规则。

2. 自定义反编译类编写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @Description 自定义日期的反序列化,如果接口返回的为”00000000“返回null
* @Author Jungle
* @DATE 2021/12/20
**/
public class DateDerializerCustom extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonToken currentToken = jp.getCurrentToken();
if (currentToken == JsonToken.VALUE_STRING) {
return ctxt.readValue(jp, String.class).equals("00000000")?null:ctxt.readValue(jp, Date.class);
}
return null;
}
}

实际上,就是继承了JsonDeserilizer找个抽象类,重写反序列化的方法,可能直接看自定义的反编译类,有业务场景,不够通用,下面看一个底层Jackson自己实现的StringDerializer帮助理解;

3. StringDeserializer反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
if (p.hasToken(JsonToken.VALUE_STRING)) {
return p.getText();
}
JsonToken t = p.getCurrentToken();
// [databind#381]
if (t == JsonToken.START_ARRAY) {
return _deserializeFromArray(p, ctxt);
}
// need to gracefully handle byte[] data, as base64
if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
Object ob = p.getEmbeddedObject();
if (ob == null) {
return null;
}
if (ob instanceof byte[]) {
return ctxt.getBase64Variant().encode((byte[]) ob, false);
}
// otherwise, try conversion using toString()...
return ob.toString();
}
// allow coercions for other scalar types
// 17-Jan-2018, tatu: Related to [databind#1853] avoid FIELD_NAME by ensuring it's
// "real" scalar
if (t.isScalarValue()) {
String text = p.getValueAsString();
if (text != null) {
return text;
}
}
return (String) ctxt.handleUnexpectedToken(_valueClass, p);
}

可以看出,这里如果是个单字符串的值,其他的比如数组就会另外处理:”[],{}”。
相关参数说明:
JonParser:定义用于读取 JSON 内容的公共 API 的基类。 实例是使用JsonFactory实例的工厂方法创建(详情可阅读其源码,其包含很多的method)
DeserializationContext :JSON的上下文类。

小结

根据业务实现反编译的规则实际上是不难的,但是如果涉及到复杂的对象反编译,还是需要公司有统一规范去编写,不可能是你一个处理,我一个处理的松散编程(实际上,小公司同一个项目都是你一个我一个的用)


参考资料