I've just converted over many of my Dates to LocalDateTime per the new(ish) java 8 time package. I've enjoyed the switch so far until I started trying to serialize and deserialize.
How can I configure Jackson to support them?:
LocalDateTime --serialize--> UTC Timestamp --deserialize--> LocalDateTime?
There's plenty of material on here about converting to formatted strings, but I can't seem to find an out-of-the-box solution to utc timestamps.
You can custom a serializer and a deserializer for LocalDateTime
, for exmaple:
CustomLocalDateTimeSerializer
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class CustomLocalDateTimeSerializer extends StdSerializer<LocalDateTime> {
protected CustomLocalDateTimeSerializer(Class<LocalDateTime> t) {
super(t);
}
protected CustomLocalDateTimeSerializer() {
this(null);
}
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider sp)
throws IOException {
Long epoch = value.atZone(ZoneId.systemDefault()).toInstant().getEpochSecond();
gen.writeString(epoch.toString());
}
}
CustomLocalDateTimeDesSerializer:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
class CustomLocalDateTimeDesSerializer extends StdDeserializer<LocalDateTime> {
protected CustomLocalDateTimeDesSerializer() {
this(null);
}
protected CustomLocalDateTimeDesSerializer(Class<LocalDateTime> t) {
super(t);
}
@Override
public LocalDateTime deserialize(JsonParser jsonparser, DeserializationContext context)
throws IOException {
Long timestamp = Long.parseLong(jsonparser.getText());
return LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault());
}
}
And use the them:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import java.time.LocalDateTime;
public class RestObject {
private LocalDateTime timestamp = LocalDateTime.now();
@JsonSerialize(using = CustomLocalDateTimeSerializer.class)
@JsonDeserialize(using = CustomLocalDateTimeDesSerializer.class)
public LocalDateTime getTimestamp() {
return timestamp;
}
public static void main(String[] args) throws Exception {
ObjectMapper objectMapper = new ObjectMapper();
// {"timestamp":"1549026058"}
System.out.println(objectMapper.writeValueAsString(new RestObject()));
}
}
The following solution solves the task of serialize/deserialise the LocalDateTime
to the timestamp and relevant at least for spring-boot v1.5 and also includes next points that are not taken into account in the @xingbin's answer:
java.util.Date
have to use the toInstant().toEpochMilli()
instead the toInstant().getEpochSecond()
null
the value to deserializeObjectMapper
The timestamp serializer class:
public class LocalDateTimeSerializer extends StdSerializer<LocalDateTime> {
private static final ZoneId DEFAULT_ZONE_ID = ZoneId.of("UTC");
public LocalDateTimeSerializer() {
super(LocalDateTime.class);
}
@Override
public void serialize(final LocalDateTime value,
final JsonGenerator generator,
final SerializerProvider provider) throws IOException {
if (value != null) {
final long mills = value.atZone(DEFAULT_ZONE_ID).toInstant().toEpochMilli();
generator.writeNumber(mills);
} else {
generator.writeNull();
}
}
}
The timestamp deserializer class:
public class LocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
private static final ZoneId DEFAULT_ZONE_ID = ZoneId.of("UTC");
public LocalDateTimeDeserializer() {
super(LocalDateTime.class);
}
@Override
public LocalDateTime deserialize(final JsonParser parser,
final DeserializationContext context) throws IOException {
final long value = parser.getValueAsLong();
return LocalDateTime.ofInstant(Instant.ofEpochMilli(value), DEFAULT_ZONE_ID);
}
}
The object mapper configuration:
@Configuration
public class ObjectMapperConfiguration {
@Bean
public ObjectMapper objectMapper() {
final ObjectMapper objectMapper = new ObjectMapper();
final SimpleModule module = new SimpleModule();
module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
objectMapper.registerModule(module);
return objectMapper;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With