DEV Community

loading...

Customize how Jackson does LocalDate Parsing

scottshipp profile image scottshipp Originally published at code.scottshipp.com ・3 min read

Jackson is the most widely used JSON parser / generator library in the Java ecosystem, and is a default dependency of Spring Boot.

Dates come in many formats and that’s a problem when we want to go from a date field in JSON to a Java LocalDate object.

Girl at chalkboard

What does that mean?

For example, in GearBuddy, my outdoor gear management application, you can track when you bought each piece of gear with the purchaseDate field. This is a date without a time, and the idea is that you put in May 24, 1995 or whenever you bought it.

JSON has no “date” type so you pass a String for the date in the request. In GearBuddy, the request looks like this (notice the last field):

{
    "type": "Tent",
    "model": "Montana",
    "brand": "Coleman",
    "size": "8-person",
    "purchaseDate": "10/21/2016"
}

The only problem is that if you send that request to the application it results in the following error:

{
    "timestamp": "2019-06-07T21:19:47.500+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "JSON parse error: Cannot deserialize value of type `java.time.LocalDate` from String \"10/21/2016\": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '10/21/2016' could not be parsed at index 0; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.time.LocalDate` from String \"10/21/2016\": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '10/21/2016' could not be parsed at index 0\n at [Source: (PushbackInputStream); line: 6, column: 18] (through reference chain: org.gearbuddy.requests.PostGearRequestBody[\"purchaseDate\"])",
    "path": "/gear"
}

Domestic quarrel. From Barber, John W. The Handbook of Illustrated Proverbs (New York, NY: George F. Tuttle, 1857)

Why that won’t work

The reason the application gets angry isn’t perfectly clear from the error message, but it’s because the date string that was passed (“10/21/2016”) isn’t in a format Jackson can parse. That’s probably odd to some of you because “10/21/2016” is the common way of writing a date in the United States.

But Jackson by default only understands the international format of yyyy-MM-dd which means that if we change our request to “2016-10-21” then the request would work fine.

Which might not be helpful to our application’s users.

So Change it

You can (of course!) change the format Jackson understands.

The way to do that is to add a @JsonFormat annotation to the field in question in your Java object.

@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
public PostGearRequestBody(
        @JsonProperty("type") String type, 
        @JsonProperty("brand") String brand,
        @JsonProperty("model") String model, 
        @JsonProperty("color") String color,
        @JsonProperty("size") String size, 
        @JsonProperty("purchasePlace") String purchasePlace,
        @JsonProperty("purchasePrice") String purchasePrice,
        @JsonProperty("purchaseDate") @JsonFormat(pattern = "MM/dd/yyyy") LocalDate purchaseDate,
        @JsonProperty("warranty") String warranty) {
    // ... etc
}

Notice that the second to last method parameter has both an @JsonProperty annotation and a JsonFormat annotation:

// ...
@JsonProperty("purchaseDate") @JsonFormat(pattern = "MM/dd/yyyy") LocalDate purchaseDate,
// ...

You can read more about @JsonFormat in the Jackson API documentation.

With that change in place, and compiling the application, the request with the date formatted as “10/21/2016” now works successfully!

Note: Originally published at code.scottshipp.com.

Discussion

pic
Editor guide