1
votes

I created a Spring web application that offers REST API. I'm using Spring Boot 2.x, Spring Data REST, Spring HATEOAS, Spring JPA, Hibernate 5.3, Mysql.

I'm also using jackson-datatype-jsr310.

I'm working with UTC dates in the entire application and I'm storing UTC dates in Mysql.

In my bean dates are defined as:

    @NotNull
    @Column(nullable = false)
    private Instant validUntil;

Everything works but when the API are consumed dates are returned in this format:

"validUntil" : "2019-11-05T22:59:59.999999Z"

I want to truncate every datetime in my application to milliseconds, so the date should be:

"validUntil" : "2019-11-05T22:59:59.999Z"

In my @Configuration I'm using a custom JacksonModule:

    @Bean
    public Module customJacksonModule() {
        SimpleModule customJacksonModule = new SimpleModule();
        customJacksonModule.addSerializer(ConstraintViolationException.class, constraintViolationExceptionSerializer());
        customJacksonModule.addSerializer(ValidationException.class, validationExceptionSerializer());
        customJacksonModule.addSerializer(cloud.optix.server.exceptions.ValidationException.class, customValidationExceptionSerializer());  
        return customJacksonModule;
    }

I tried several approaches but noone worked so far:

1. Application.properties

I put spring.jackson.serialization.write-date-timestamps-as-nanoseconds = false in my application.propreties

2. Custom ObjectMapper

I tried to change objectMapper's settings.

@Autowired(required = true)
public void configureJackson(ObjectMapper jackson2ObjectMapper) {
    jackson2ObjectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    jackson2ObjectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false);
}

3. Removed @EnableHypermediaSupport

@EnableHypermediaSupport(type = {EnableHypermediaSupport.HypermediaType.HAL})

All those actions didn't change how the date is returned to the client. I'd like some advice to understand/debug why the date is not returned in the right format.

As additional note, I found that in com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializerBase in serialize() method I've neither useNanoseconds property, nor default formatter. However the property spring.jackson.serialization.write-dates-as-timestamps in application.properties has effect on the date format.

enter image description here

3

3 Answers

1
votes

Did you try just annotating your validUntil field with @JsonFormat like below.

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
@NotNull
@Column(nullable = false)
private Instant validUntil;
1
votes

You can configure your ObjectMapper to use a specific date format cross the application:

jackson2ObjectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz"));
1
votes

You need to replace InstantSerializer is being used by Jackson.

@Configuration
public class JacksonConfig {

    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
        return builder -> builder.serializers(new CustomInstantSerializer());
    }

    private static class CustomInstantSerializer extends InstantSerializer {

        private static final DateTimeFormatter UTC_DATE_FORMAT = DateTimeFormatter
                .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZone(ZoneOffset.UTC);

        private CustomInstantSerializer() {
            super(InstantSerializer.INSTANCE, null, UTC_DATE_FORMAT);
        }

    }

}

You can put JacksonConfig class in the config package of the project, for example.

Also, if it will not work, check that SpringBoot can really find it. Set a break point in the default constructor of InstantSerializer or add temporal logging there.