Spring uses Jackson's InstantSerializer to write out my Instant fields like this:
now: "2021-04-07T10:51:53.043320Z"
I don't want the nanoseconds, though - just the milliseconds. I guessed that setting the application property
spring.jackson.serialization.write-date-timestamps-as-nanoseconds=false
would achieve this, but it makes no difference.
How can I tell Spring/Jackson to omit the nanoseconds when serializing Instants?
(I'm using Spring Boot 2.2.11.RELEASE)
Update
I eventually got it work, based on this answer. I had to use the deprecated JSR310Module instead of JavaTimeModule, and override the createContextual(...) method to force it to always use my serializer.
@Bean
public ObjectMapper objectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
JSR310Module jsr310Module = new JSR310Module();
jsr310Module.addSerializer(Instant.class, new MyInstantSerializer());
objectMapper.registerModule(jsr310Module);
return objectMapper;
}
private static class MyInstantSerializer extends InstantSerializer {
public MyInstantSerializer() {
super(InstantSerializer.INSTANCE, false, false,
new DateTimeFormatterBuilder().appendInstant(3).toFormatter());
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
return this;
}
}
And this works too (based on Volodya's answer below):
@Bean
public Jackson2ObjectMapperBuilderCustomizer addCustomTimeSerialization() {
return jacksonObjectMapperBuilder ->
jacksonObjectMapperBuilder.serializerByType(Instant.class, new JsonSerializer<Instant>() {
private final DateTimeFormatter formatter =
new DateTimeFormatterBuilder().appendInstant(3).toFormatter();
@Override
public void serialize(
Instant instant, JsonGenerator generator, SerializerProvider provider) throws IOException {
generator.writeString(formatter.format(instant));
}
});
}
For that you could use @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS]")
Full example:
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
public class Message {
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS]")
private final LocalDateTime dateTime;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS]")
private final ZonedDateTime zonedDateTime;
public Message(ZonedDateTime zonedDateTime) {
this(zonedDateTime.toLocalDateTime(), zonedDateTime);
}
public Message(LocalDateTime dateTime, ZonedDateTime zonedDateTime) {
this.dateTime = dateTime;
this.zonedDateTime = zonedDateTime;
}
public LocalDateTime getDateTime() {
return dateTime;
}
public ZonedDateTime getZonedDateTime() {
return zonedDateTime;
}
}
Test:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import java.time.*;
import java.time.temporal.ChronoUnit;
import static org.junit.jupiter.api.Assertions.*;
@JsonTest
class MessageTest {
@Autowired
ObjectMapper mapper;
@Test
public void serializationTest() throws JsonProcessingException {
final LocalDate date = LocalDate.of(2000, Month.JANUARY, 1);
final LocalTime time = LocalTime.of(12, 20, 10).plus(123, ChronoUnit.MILLIS);
final Message message = new Message(ZonedDateTime.of(date, time, ZoneId.systemDefault()));
final String res = mapper.writeValueAsString(message);
assertEquals("{\"dateTime\":\"2000-01-01T12:20:10.123\",\"zonedDateTime\":\"2000-01-01T12:20:10.123\"}", res);
}
}
Update:
If you want to configure it centrally you could:
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"));
@SpringBootApplication
public class InstantSerializerApplication {
public static void main(String[] args) {
SpringApplication.run(InstantSerializerApplication.class, args);
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer addCustomTimeSerialization() {
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.serializerByType(ZonedDateTime.class, new JsonSerializer<ZonedDateTime>() {
@Override
public void serialize(ZonedDateTime zonedDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
jsonGenerator.writeString(formatter.format(zonedDateTime));
}
});
}
}