0

I have a class with a field of type LocalDate:

public class MyClass {
    private LocalDate myDate;
}

I have to store the value as yyyy-MM-dd instead of as [yyyy, M, d]

Which can be done creating a LocalDateSerializer as indicated in https://stackoverflow.com/a/38731094/10850340 (DateTimeFormatter.ISO_LOCAL_DATE)

In my scenario, there are multiple sources from where the data is received. One of them sends the date as ISO_INSTANT '2011-12-03T10:15:30Z'. The LocalDateDeserializer:

public class LocalDateDeserializer extends StdDeserializer<LocalDate> {

    private static final long serialVersionUID = 1L;
    
    protected LocalDateDeserializer() {
        super(LocalDate.class);
    }
    
    @Override
    public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        return LocalDate.parse(jp.readValueAs(String.class));
    }

}

throws the following WARN:

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Text '2021-02-25T00:52:00.000Z' could not be parsed, unparsed text found at index 10; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Text '2021-02-25T00:52:00.000Z' could not be parsed, unparsed text found at index 10 (through reference chain: MyClass["myDate"])]

My understanding is that the received string is not parseable as LocalDate: LocalDate.parse(jp.readValueAs(String.class));

What would be the best way to have LocalDateDeserializer accepting any valid date format but returning a LocalDate

Machavity
  • 30,841
  • 27
  • 92
  • 100
j.xavier.atero
  • 506
  • 2
  • 10
  • 25
  • If the string is `2012-04-15T22:05:43Z`, then it’s already April 16 in my time zone, so not the date that one can read from the string. Which date do you want in this case? – Ole V.V. Feb 25 '21 at 04:42
  • I see, I did not consider that scenario. In my case it would be the date of the string ; the date is entered by a user who only selects the day, and not the time. But then the data is received with irrelevant time information. – j.xavier.atero Feb 25 '21 at 13:50

3 Answers3

2

If you just need to convert the date from a Date object into a LocalDate, when populating the MyClass instance then :

Date input = new Date();
LocalDate date = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();

java.util.Date represents an instant on the time-line, not a "date".

But in java 9, you could do something like :

LocalDate date = LocalDate.ofInstant(input.toInstant(), ZoneId.systemDefault());

Instead of making the conversion in the de-serializer, you could handle it before that in the mapper method itself before setting the value to MyClass instance, since you have multiple sources with multiple date time formats being used. Otherwise you will have to handle conditions in the de serializer to handle the different formats in which you are going to receive the date. If you handle it in the mapper method where you actually set the data to the entity (considering that you have a DTO class), you could provide specific implementation for the different date formats from the different sources.

If you still want to use the deserializer use a DateTimeFormatter for the different date formats and set it accordingly :

@Override
public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd");

    if (**somecondition**) {
       dtf = DateTimeFormatter.ofPattern("yyyy-MMM-dd");
    }
    
    LocalDate dt = dtf.parseLocalDate(yourinput);
    return dt;
}
Ananthapadmanabhan
  • 5,706
  • 6
  • 22
  • 39
2

An optional part of the format

You may use an optional part in your format pattern string. Such is enclosed in square brackets and denotes a part of the date-time string that may be present or absent during parsing. Like this:

private static final DateTimeFormatter inputFormatter
        = DateTimeFormatter.ofPattern("uuuu-MM-dd['T'HH:mm:ssX]");

If you’re sure that appending the irrelevant time and offset (Z is a UTC offset) has not changed the date, you may now parse like this:

    for (String inputString
            : new String[] { "2011-12-03T10:15:30Z", "2013-11-21" }) {
        LocalDate date = LocalDate.parse(inputString, inputFormatter);
        System.out.format("%-20s parsed to %s%n", inputString, date);
    }

Output:

2011-12-03T10:15:30Z parsed to 2011-12-03
2013-11-21           parsed to 2013-11-21
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
0

For my particular scenario I know that there are only two kinds of formatting in which dates can come in. I can check which format it is and process it accordingly.

I am posting what works in my scenario, but I am sure that there are more "robust" solutions.

public LocalDate deserialize(JsonParser jp, DeserializationContext ctxt)
        throws IOException, JsonProcessingException {
    String input_date = jp.readValueAs(String.class);
    boolean is_date_time = input_date.contains("T");
    if (is_date_time) {
        int end = input_date.indexOf('T');
        input_date = input_date.substring(0, end);
    }
    return LocalDate.parse(input_date);
}
j.xavier.atero
  • 506
  • 2
  • 10
  • 25