How to make milliseconds optional in @JsonFormat for Timestamp parsing with Jackson?

19,014

Solution 1

If you are using Java 8 Try specifying .SSS inside square brackets [.SSS]

JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS]X")

Solution 2

If millis consist of 1 or 2 or 3 digit you can use this pattern

JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss[.SSS][.SS][.S]X")

Optional section order strict

Solution 3

For those of you that were not able to get the [.SSS] solution to work, here is what I ended up doing.

Retain the @JsonFormat annotation on your field for serialization, but build a custom deserializer for parsing Dates which might not have the milliseconds portion specified. Once you implement the deserializer you will have to register it with your ObjectMapper as a SimpleModule

class DateDeserializer extends StdDeserializer<Date> {

    private static final SimpleDateFormat withMillis = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
    private static final SimpleDateFormat withoutMillis = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");

    public DateDeserializer() {
        this(null);
    }

    public DateDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String dateString = p.getText();
        if (dateString.isEmpty()) {
            //handle empty strings however you want,
            //but I am setting the Date objects null
            return null;
        }

        try {
            return withMillis.parse(dateString);
        } catch (ParseException e) {
            try {
                return withoutMillis.parse(dateString);
            } catch (ParseException e1) {
                throw new RuntimeException("Unable to parse date", e1);
            }
        }
    }
}

Now that you have a custom deserializer, all that is left is to register it. I am doing so with a ContextResolver<ObjectMapper> that I already had in my project, but however you work with your ObjectMapper should be fine.

@Provider
class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {

    private final ObjectMapper mapper;

    public ObjectMapperContextResolver() {
        mapper = new ObjectMapper();
        SimpleModule dateModule = new SimpleModule();
        dateModule.addDeserializer(Date.class, new DateDeserializer());
        mapper.registerModule(dateModule);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return mapper;
    }
}
Share:
19,014

Related videos on Youtube

onkami
Author by

onkami

Just a furry.

Updated on June 04, 2022

Comments

  • onkami
    onkami almost 2 years

    I have the following declaration in my object:

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX")
    private ZonedDateTime start;
    

    And when I parse timestamps like 2016-12-08T12:16:07.124Z (using Jackson Json De-serilaizer) it works fine, but once I receive timestamps without milliseconds (such as "2016-12-08T12:16:07Z"), it throws exception.

    How I can possibly make milliseconds optional in format specification?

  • onkami
    onkami over 7 years
    Worked! Thanks!
  • k_o_
    k_o_ over 7 years
    Does not work on Java 8 version 102. The brackets are also not mentioned in the JavaDoc: docs.oracle.com/javase/8/docs/api/java/text/…
  • Luke
    Luke almost 6 years
    Very useful information on how to register a custom ObjectMapper in a JEE application.
  • user1697575
    user1697575 over 4 years
    [.SSS] works only for a 3 digit millisecond values. if its one digit or 2 digit it breaks. E.g. 2019-03-24T16:14:08.23 will need "yyyy-MM-dd'T'HH:mm:ss[.SS] pattern