I was never able to get this to work simple using annotations. To get it to work, I created a ContextResolver for ObjectMapper, then I added the JSR310Module (update: now it is JavaTimeModule instead), along with one more caveat, which was the need to set write-date-as-timestamp to false. See more at the documentation for the JSR310 module. Here's an example of what I used.

Dependency

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.4.0</version>
</dependency>

Note: One problem I faced with this is that the jackson-annotation version pulled in by another dependency, used version 2.3.2, which cancelled out the 2.4 required by the jsr310. What happened was I got a NoClassDefFound for ObjectIdResolver, which is a 2.4 class. So I just needed to line up the included dependency versions

ContextResolver

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {  
    private final ObjectMapper MAPPER;

    public ObjectMapperContextResolver() {
        MAPPER = new ObjectMapper();
        // Now you should use JavaTimeModule instead
        MAPPER.registerModule(new JSR310Module());
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return MAPPER;
    }  
}

Resource class

@Path("person")
public class LocalDateResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getPerson() {
        Person person = new Person();
        person.birthDate = LocalDate.now();
        return Response.ok(person).build();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createPerson(Person person) {
        return Response.ok(
                DateTimeFormatter.ISO_DATE.format(person.birthDate)).build();
    }

    public static class Person {
        public LocalDate birthDate;
    }
}

Test

curl -v http://localhost:8080/api/person
Result: {"birthDate":"2015-03-01"}

curl -v -POST -H "Content-Type:application/json" -d "{\"birthDate\":\"2015-03-01\"}" http://localhost:8080/api/person
Result: 2015-03-01


See also here for JAXB solution.

UPDATE

The JSR310Module is deprecated as of version 2.7 of Jackson. Instead, you should register the module JavaTimeModule. It is still the same dependency.

Answer from Paul Samsotha on Stack Overflow
Top answer
1 of 16
170

I was never able to get this to work simple using annotations. To get it to work, I created a ContextResolver for ObjectMapper, then I added the JSR310Module (update: now it is JavaTimeModule instead), along with one more caveat, which was the need to set write-date-as-timestamp to false. See more at the documentation for the JSR310 module. Here's an example of what I used.

Dependency

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.4.0</version>
</dependency>

Note: One problem I faced with this is that the jackson-annotation version pulled in by another dependency, used version 2.3.2, which cancelled out the 2.4 required by the jsr310. What happened was I got a NoClassDefFound for ObjectIdResolver, which is a 2.4 class. So I just needed to line up the included dependency versions

ContextResolver

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {  
    private final ObjectMapper MAPPER;

    public ObjectMapperContextResolver() {
        MAPPER = new ObjectMapper();
        // Now you should use JavaTimeModule instead
        MAPPER.registerModule(new JSR310Module());
        MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return MAPPER;
    }  
}

Resource class

@Path("person")
public class LocalDateResource {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getPerson() {
        Person person = new Person();
        person.birthDate = LocalDate.now();
        return Response.ok(person).build();
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public Response createPerson(Person person) {
        return Response.ok(
                DateTimeFormatter.ISO_DATE.format(person.birthDate)).build();
    }

    public static class Person {
        public LocalDate birthDate;
    }
}

Test

curl -v http://localhost:8080/api/person
Result: {"birthDate":"2015-03-01"}

curl -v -POST -H "Content-Type:application/json" -d "{\"birthDate\":\"2015-03-01\"}" http://localhost:8080/api/person
Result: 2015-03-01


See also here for JAXB solution.

UPDATE

The JSR310Module is deprecated as of version 2.7 of Jackson. Instead, you should register the module JavaTimeModule. It is still the same dependency.

2 of 16
132
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
🌐
Medium
medium.com › @smita.s.kothari › formatting-localdate-with-jacksons-jsonformat-annotation-in-java-5e53fd05d0b4
Formatting LocalDate with Jackson’s @JsonFormat Annotation in Java | by Smita Kothari | Medium
March 5, 2025 - The @JsonFormat(pattern = "yyyy-MM-dd") annotation is applied to the birthDate field. This ensures that when the Person object is serialized to JSON, the LocalDate will follow the format yyyy-MM-dd (e.g., 1990-05-15).
🌐
Kodejava
kodejava.org › how-to-format-localdate-object-using-jackson
How to format LocalDate object using Jackson? - Learn Java by Examples
June 1, 2024 - Let’s create a simple class that convert Recording object into JSON string and apply the date formatter defined in the LocalDateSerializer class. package org.kodejava.jackson; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.kodejava.jackson.support.Recording; import java.time.LocalDate; import java.time.Month; public class RecordingToJson { public static void main(String[] args) { Recording recording = new Recording(); recording.setId(1L); recording.setTitle("Twist and Shout"); recording.setReleaseDate(LocalDate.of(1964, Month.FEBRUARY, 3)); ObjectMapper mapper = new ObjectMapper(); try { String json = mapper.writeValueAsString(recording); System.out.println("JSON = " + json); } catch (JsonProcessingException e) { e.printStackTrace(); } } }
🌐
Reflectoring
reflectoring.io › configuring-localdate-serialization-spring-boot
Serializing LocalDate to JSON in Spring Boot
July 25, 2017 - ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); The module teaches the ObjectMapper how to work with LocalDates and the parameter WRITE_DATES_AS_TIMESTAMPS tells the mapper to represent a Date as a String in JSON.
🌐
DEV Community
dev.to › kevinblandy › formatting-json-datelocaldatetimelocaldate-in-spring-boot-odf
Formatting json Date/LocalDateTime/LocalDate in Spring Boot - DEV Community
September 21, 2022 - Spring Boot uses jackson by default to serialize, deserialize json data. By default, Jackson serializes Date objects as timestamps. For LocalDateTime, LocalDate objects, jackson doesn't do anything special, it just treats them as basic Java objects. import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; import java.util.HashMap; import java.util.Map; import com.fasterxml.jackson.databind.ObjectMapper; public class MainTest { public static void main(String...
🌐
Baeldung
baeldung.com › home › json › jackson › jackson date
Jackson Date | Baeldung
May 11, 2024 - In order to be able to handle LocalDate, we need to register the JavaTimeModule with our ObjectMapper. We also need to disable the feature WRITE_DATES_AS_TIMESTAMPS in ObjectMapper to prevent Jackson from adding time digits to the JSON output: ...
🌐
Medium
medium.com › @skywalkerhunter › the-practical-jackson-deserialize-java-8-localdate-with-json-serialization-bdb0870f062e
The Practical Jackson— Deserialize Java 8 LocalDate with JSON Serialization | by Oli H. | Medium
April 6, 2019 - Here’s a quick fix: // 1st one for the LocalDate, the 2nd one for LocalDateTime @JsonFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd HH:mm") PS: Usually in your system there are a LOT of dateTimes.
🌐
Mkyong
mkyong.com › home › java › jackson java 8 date/time type `java.time.localdate` not supported by default
Jackson Java 8 date/time type `java.time.LocalDate` not supported by default - Mkyong.com
April 26, 2024 - ... ObjectMapper om = new ObjectMapper(); Book book = new Book(1L, "Book A", BigDecimal.valueOf(9.99), LocalDate.of(2023, 10, 1)); // convert object to json String result = om.writeValueAsString(book);
🌐
Google Groups
groups.google.com › g › play-framework › c › Dv-IpvBqWgo
How do I use java.time.LocalDate on JSON Rests?
September 30, 2015 - Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message ... java.lang.RuntimeException: com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class java.time.LocalDate] from String value ('2015-09-30'); no single-String constructor/factory method at [Source: N/A; line: -1, column: -1] (through reference chain: models.Person["dayOfBirth"])
Find elsewhere
🌐
DEV Community
dev.to › scottshipp › customize-how-jackson-does-localdate-parsing-2hc5
Customize how Jackson does LocalDate Parsing - DEV Community
October 7, 2019 - 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" }
🌐
Java Code Geeks
javacodegeeks.com › home › core java
Gson Support for Java 8 Date-Time Types - Java Code Geeks
June 6, 2024 - Serialization: The serialize method takes a LocalDateTime object and converts it to a JSON primitive string using the ISO-8601 format.
🌐
Java Code Geeks
javacodegeeks.com › home › core java
Jackson & Java 8 Date-Time: LocalDate Support Issues - Java Code Geeks
April 3, 2024 - It doesn’t know how to convert a LocalDate object into a JSON representation. Consequently, it throws an exception indicating that no serializer was found for the LocalDate class. To resolve this issue, you can either create a custom serializer and deserializer for LocalDate, or you can use third-party modules like jackson-datatype-jsr310, which provides support for Java 8 date/time types in Jackson.
🌐
Java Code Geeks
javacodegeeks.com › home
Jackson Serialize and Deserialize LocalDate Example - Java Code Geeks
June 7, 2024 - In this step, I will create a Person.java class which has four data members. Note: the Lombok annotations are used to reduce the boilerplate code. ... localDateJsr310 – a LocalDate object annotated with @JsonFormat with LocalDateSerializer and LocalDateDeserializer.
🌐
LabEx
labex.io › tutorials › java-how-to-deserialize-dates-from-json-450995
How to deserialize dates from JSON | LabEx
public class DateValidator { public static boolean isValidDate(String dateString) { try { LocalDate.parse(dateString, DateTimeFormatter.ISO_DATE); return true; } catch (DateTimeParseException e) { return false; } } public static LocalDate sanitizeDate(String dateString) { try { return LocalDate.parse(dateString, DateTimeFormatter.ISO_DATE); } catch (DateTimeParseException e) { // Fallback or default date return LocalDate.now(); } } } ... LabEx suggests practicing these advanced techniques through progressive complexity exercises to master date deserialization challenges. By mastering JSON date deserialization techniques in Java, developers can create more flexible and reliable data parsing solutions.
🌐
Javacodemonk
javacodemonk.com › java-8-date-time-json-formatting-with-jackson-5fe5ff13
Java 8 date time JSON formatting with Jackson
October 24, 2019 - import com.fasterxml.jackson.annotation.JsonFormat; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Date; public class SampleDto { @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "Asia/Kolkata") private Instant instant; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ") private Date date; @JsonFormat(pattern = "yyyy-MM-dd") private LocalDate localDate; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") private LocalDateTime localDateTime; //Getters and Setter omitted for brevity } We will use this POJO for Jackson based JSON serialization/deserialization. It is important to note here that we need to specify the timezone while dealing with java.time.Instant otherwise we will encounter the below exception:
🌐
Java Guides
javaguides.net › 2019 › 11 › gson-localdatetime-localdate.html
GSON LocalDateTime, LocalDate Serialization and Deserialization Example
September 23, 2024 - LocalDateTimeDeserializer · Check out Convert JSON to Java Object using GSON · Check out Convert Java Object to JSON using GSON · Let's first define an object to be serialized and deserialized - Order.java · class Order { private int id; private String orderName; private String orderDesc; private LocalDate orderCreatedDate; private LocalDateTime orderCreatedDateTime; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getOrderName() { return orderName; } public void setOrderName(String orderName) { this.orderName = orderName; } public String getOrder
Top answer
1 of 2
2

The LocalDate is stored in incorrectly in the JSON.

The correct way to fix the problem is to ensure that the ObjectMapper generating the JSON has the JavaTimeModule or the Jdk8Module registered. This ensures that the LocalDate is correctly serialized to a JSON array of [year, month, day].

The brute-force method is to follow the solution that @Roy outlined in another answer, and convert each relevant field manually to a LocalDate. Something along the lines of :

LocalDate date = LocalDate.now()
        .with(ChronoField.YEAR, year)
        .with(ChronoField.MONTH_OF_YEAR, Month.valueOf(month).getValue())
        .with(ChronoField.DAY_OF_MONTH, dayOfMonth);

This is obviously rather error-prone, and not how the classes were intended to be used.

2 of 2
1

Replace your MyCustomObject.class like this:

==================================
package ;
public class Chronology
{
    private String calendarType;

    private String id;

    public void setCalendarType(String calendarType){
        this.calendarType = calendarType;
    }
    public String getCalendarType(){
        return this.calendarType;
    }
    public void setId(String id){
        this.id = id;
    }
    public String getId(){
        return this.id;
    }
}

==================================
package ;
public class From
{
    private int year;

    private String month;

    private String era;

    private int dayOfMonth;

    private String dayOfWeek;

    private int dayOfYear;

    private boolean leapYear;

    private int monthValue;

    private Chronology chronology;

    public void setYear(int year){
        this.year = year;
    }
    public int getYear(){
        return this.year;
    }
    public void setMonth(String month){
        this.month = month;
    }
    public String getMonth(){
        return this.month;
    }
    public void setEra(String era){
        this.era = era;
    }
    public String getEra(){
        return this.era;
    }
    public void setDayOfMonth(int dayOfMonth){
        this.dayOfMonth = dayOfMonth;
    }
    public int getDayOfMonth(){
        return this.dayOfMonth;
    }
    public void setDayOfWeek(String dayOfWeek){
        this.dayOfWeek = dayOfWeek;
    }
    public String getDayOfWeek(){
        return this.dayOfWeek;
    }
    public void setDayOfYear(int dayOfYear){
        this.dayOfYear = dayOfYear;
    }
    public int getDayOfYear(){
        return this.dayOfYear;
    }
    public void setLeapYear(boolean leapYear){
        this.leapYear = leapYear;
    }
    public boolean getLeapYear(){
        return this.leapYear;
    }
    public void setMonthValue(int monthValue){
        this.monthValue = monthValue;
    }
    public int getMonthValue(){
        return this.monthValue;
    }
    public void setChronology(Chronology chronology){
        this.chronology = chronology;
    }
    public Chronology getChronology(){
        return this.chronology;
    }
}

==================================
package ;
public class MyCustomObject
{
    private From from;

    public void setFrom(From from){
        this.from = from;
    }
    public From getFrom(){
        return this.from;
    }
}
🌐
Codidact
software.codidact.com › posts › 292870
LocalDate format in fields stored as json via Hibernate - Software Development
@Entity public class E { @Id int id; @JdbcTypeCode(SqlTypes.JSON) Dto jsonValue; } public class Dto { LocalDate myDate; } ... It is possible to set custom JSON serializer for hibernate. ... @Configuration public class JsonHibernatePropertiesCustomizerConfig { @Bean HibernatePropertiesCustomizer customizer(ObjectMapper objectMapper) { return map -> map.put(AvailableSettings.JSON_FORMAT_MAPPER, new JacksonJsonFormatMapper(objectMapper)); } }