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。
參考資料: