Jackson 可以對 Date 綁定,缺點是 Json 用了毫秒。由於一般儲存到資料庫都是用 Unix 時間戳,計算單位是秒,這時候其實不適用。
// Foo.java
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
@Data
class Foo {
@JsonProperty("create_time")
private Date createTime;
public static void main(String[] args) throws Throwable {
Foo foo = new Foo();
foo.setCreateTime(new Date());
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(foo); // {"create_time":1568024055895}
Foo foo2 = mapper.readValue(s, Foo.class); // Foo(createTime=Mon Sep 09 18:14:15 CST 2019)
foo.equals(foo2); // true
}
}
所以需要自己寫轉換類。
// Foo.java
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
@Data
class Foo {
@JsonProperty("create_time")
@JsonDeserialize(using = DefaultDateDeserializer.class)
@JsonSerialize(using = DefaultDateSerializer.class)
private Date createTime;
}
// DefaultDateDeserializer.java
import java.io.IOException;
import java.util.Date;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
class DefaultDateDeserializer extends JsonDeserializer {
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return new Date(p.getLongValue() * 1000);
}
}
// DefaultDateSerializer.java
import java.io.IOException;
import java.util.Date;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
class DefaultDateSerializer extends JsonSerializer {
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeNumber(value.toInstant().getEpochSecond());
}
}
這時候的結果如下,會發現 Json 是我們要的,但是最後是 false。
原因是創建 Date 實例含有毫秒,不過轉到 Json 的時候我們去掉了毫秒;
從 Json 轉回 Date 則損失了這些毫秒,所以兩者才會不相等。
{"create_time":1568024055}
Foo(createTime=Mon Sep 09 18:14:15 CST 2019)
false
那麼接著要把 Date 改成 Instant,因為 Date 很多方法都棄用了,Instant 有很多方法比較好用,而且轉成 ISO8601 或是 epoch timestamp 比較方便。
// build.gradle
implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.9"
// Foo.java
import java.time.Instant;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;
@Data
class Foo {
@JsonProperty("create_time")
@JsonDeserialize(using = DefaultInstantDeserializer.class)
@JsonSerialize(using = DefaultInstantSerializer.class)
private Instant createTime;
public static void main(String[] args) throws Throwable {
Foo foo = new Foo();
foo.setCreateTime(Instant.now());
ObjectMapper mapper = new ObjectMapper();
String s = mapper.writeValueAsString(foo); // {"create_time":1568024055}
Foo foo2 = mapper.readValue(s, Foo.class); // Foo(createTime=2019-09-09T10:14:15Z)
foo.equals(foo2); // true
}
}
// DefaultInstantDeserializer.java
import java.io.IOException;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer;
@SuppressWarnings("serial")
class DefaultInstantDeserializer extends InstantDeserializer {
public DefaultInstantDeserializer() {
super(Instant.class,
DateTimeFormatter.ISO_INSTANT,
Instant::from,
a -> Instant.ofEpochMilli(a.value),
a -> Instant.ofEpochSecond(a.integer, a.fraction),
null,
true
);
}
@Override
public Instant deserialize(JsonParser parser, DeserializationContext context) throws IOException {
if (Objects.isNull(parser.getText()) || parser.getValueAsLong() == 0) {
return null;
}
return super.deserialize(parser, context);
}
}
// DefaultInstantSerializer.java
import java.io.IOException;
import java.time.Instant;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
class DefaultInstantSerializer extends InstantDeserializer {
@Override
public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeNumber(value.getEpochSecond());
}
}
DefaultInstantSerializer 繼承 InstantDeserializer 是因為這樣可以接受多種格式,例如 1568024055.000000000。
參考資料: