sonoerin
sonoerin

Reputation: 5185

converting json date to one of many for Jackson

I have a spring boot 1.3.5 app that uses jackson as:

"com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.5.0"

The problem I am seeing is when a user enters in a value in a different format than expected (mm/dd/yyyy) and does the POST, I get a nasty error returned to the UI (nothing shows in the server logs):

Could not read document: Text '08-21-1999' could not be parsed at index 2 (through reference chain: com.xxx.yyy.MyDTO[\"firstofficevisit\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Text '08-21-1999' could not be parsed at index 2 (through reference chain: com.xxx.yyy.MyDTO[\"firstofficevisit\"])

And here is the json being submitted where dateOfBirth works but firstofficevisit throws the error above:

{
   "patientId":3,
   "dateOfBirth":"04/11/1984",
   "firstofficevisit":"08-21-1999",
   ...
}

I have this on my DTO:

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "MM/dd/yyyy")
private LocalDate dateOfBirth;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "MM/dd/yyyy")
private LocalDate firstofficevisit;

But, unless the dates are submitted exactly like that it fails. I do have a nice Java utility that handles date conversions and I wonder if its possible to get my DTO to use this:

public class DateUtil {

    private static final Map<String, String> DATE_FORMAT_REGEXPS = new HashMap<String, String>() {{
        put("^\\d{8}$", "yyyyMMdd");
        put("^\\d{1,2}-\\d{1,2}-\\d{4}$", "dd-MM-yyyy");
        put("^\\d{4}-\\d{1,2}-\\d{1,2}$", "yyyy-MM-dd");
        put("^\\d{1,2}/\\d{1,2}/\\d{4}$", "MM/dd/yyyy");
        put("^\\d{4}/\\d{1,2}/\\d{1,2}$", "yyyy/MM/dd");
        put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$", "dd MMM yyyy");
        put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$", "dd MMMM yyyy");
        put("^\\d{12}$", "yyyyMMddHHmm");
        put("^\\d{8}\\s\\d{4}$", "yyyyMMdd HHmm");
        put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$", "dd-MM-yyyy HH:mm");
        put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy-MM-dd HH:mm");
        put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$", "MM/dd/yyyy HH:mm");
        put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy/MM/dd HH:mm");
        put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMM yyyy HH:mm");
        put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMMM yyyy HH:mm");
        put("^\\d{14}$", "yyyyMMddHHmmss");
        put("^\\d{8}\\s\\d{6}$", "yyyyMMdd HHmmss");
        put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd-MM-yyyy HH:mm:ss");
        put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy-MM-dd HH:mm:ss");
        put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "MM/dd/yyyy HH:mm:ss");
        put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy/MM/dd HH:mm:ss");
        put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMM yyyy HH:mm:ss");
        put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMMM yyyy HH:mm:ss");
    }};

My problem is that the dates are coming across as string and trying to validate every permutation for validity and format is unwieldy at best, especially since I have many date fields that will need this.

So I am not sure the best approach for this - is it possible to have Jackson be more forgiving with my DateUtil.java (and how do you wire that up!) or is there a way to enforce this on the client side, where it seems more natural? Any examples you can share would really help out

Upvotes: 1

Views: 1711

Answers (1)

Manos Nikolaidis
Manos Nikolaidis

Reputation: 22254

There is no way to automatically parse a date in any format. You will have to somehow list somewhere all the formats you want to handle in a particular order. Or enforce a particular format in the front-end if possible. You shouldn't have to preprocess the json as text to get it to parse.

The easiest way I know to handle multiple date format with Java 8 and Jackson is with a custom JsonDeserializer and multiple optional patterns. E.g.

class LocalDateParser extends JsonDeserializer<LocalDate> {

    private final DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder()
            .appendOptional(DateTimeFormatter.ofPattern("MM-dd-yyyy"))
            .appendOptional(DateTimeFormatter.ofPattern("MM/dd/yyyy"))
            .toFormatter();

    @Override
    public LocalDate deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException {
        return LocalDate.parse(jsonparser.getText(), dateFormatter);
    }
}

and annotate you fields like this:

@JsonDeserialize(using = LocalDateParser.class)
public LocalDate firstofficevisit;

Upvotes: 3

Related Questions