Java 8 LocalDate Jackson format

325,401

Solution 1

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.

Solution 2

@JsonSerialize and @JsonDeserialize worked fine for me. They eliminate the need to import the additional jsr310 module:

@JsonDeserialize(using = LocalDateDeserializer.class)  
@JsonSerialize(using = LocalDateSerializer.class)  
private LocalDate dateOfBirth;

Deserializer:

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));
    }

}

Serializer:

public class LocalDateSerializer extends StdSerializer<LocalDate> {

    private static final long serialVersionUID = 1L;

    public LocalDateSerializer(){
        super(LocalDate.class);
    }

    @Override
    public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider sp) throws IOException, JsonProcessingException {
        gen.writeString(value.format(DateTimeFormatter.ISO_LOCAL_DATE));
    }
}

Solution 3

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);

Solution 4

In Spring Boot web app, with Jackson and JSR 310 version "2.8.5"

compile "com.fasterxml.jackson.core:jackson-databind:2.8.5"
runtime "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.8.5"

The @JsonFormat works:

import com.fasterxml.jackson.annotation.JsonFormat;

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

Solution 5

The simplest solution (which supports deserialization and serialization as well) is

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy")
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate dateOfBirth;

While using the following dependencies in your project.

Maven

<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-databind</artifactId>
   <version>2.9.7</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.datatype</groupId>
   <artifactId>jackson-datatype-jsr310</artifactId>
   <version>2.9.7</version>
</dependency>

Gradle

compile "com.fasterxml.jackson.core:jackson-databind:2.9.7"
compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.7"

No additional implementation of a ContextResolver, Serializer or Deserializer is required.

Share:
325,401

Related videos on Youtube

JAB
Author by

JAB

Updated on October 08, 2021

Comments

  • JAB
    JAB over 2 years

    For java.util.Date when I do

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

    then in JSON request when I send

    { {"dateOfBirth":"01/01/2000"} }  
    

    it works.

    How should I do this for Java 8's LocalDate field??

    I tried having

    @JsonDeserialize(using = LocalDateDeserializer.class)  
    @JsonSerialize(using = LocalDateSerializer.class)  
    private LocalDate dateOfBirth;  
    

    It didn't work.

    Can someone please let me know what's the right way to do this..

    Below are dependencies

    <dependency>
        <groupId>org.jboss.resteasy</groupId>
        <artifactId>jaxrs-api</artifactId>
         <version>3.0.9.Final</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.jaxrs</groupId>
        <artifactId>jackson-jaxrs-json-provider</artifactId>
        <version>2.4.2</version>
    </dependency>
    <dependency>
        <groupId>com.wordnik</groupId>
        <artifactId>swagger-annotations</artifactId>
        <version>1.3.10</version>
    </dependency>
    
  • JAB
    JAB about 9 years
    Hi Peeskillet , the field birthDate , is being generated as "birthDate ": { "year": 0, "month": "Month", "dayOfMonth": 0, "dayOfWeek": "DayOfWeek", "era": { "value": 0 }, "dayOfYear": 0, "leapYear": false, "monthValue": 0, "chronology": { "id": "", "calendarType": "" } } how can i make it just as "birthDate"???
  • Paul Samsotha
    Paul Samsotha about 9 years
    Check the ContextResolver is called. Add a print statement in the getContext method. If this method is called, I don't see a reason for this not to work. If it's not called, then it may be something that's needs to be fixed with the app configuration. For that I would need to see more than what you have provided. Like Resteasy version, dependencies, app config either web.xml or Application subclass. Basically enough to reproduce the problem
  • JAB
    JAB about 9 years
    ContextResolver is not being called Peeskillet . I am resgistering it in web.xml as <context-param> <param-name>resteasy.resources</param-name> <param-value>com.bac.ObjectMapperContextResolver</param-valu‌​e> </context-param> updated question for dependencies i am using
  • Paul Samsotha
    Paul Samsotha about 9 years
    Swagger seems to be the issue. I would say to disable it but seeing from this question there is an issue which has been filed, with a conflict between Swagger's ObjectMapper and trying to use your own. You can try and disable theirs, and in the ContextResolver, set all the configurations to the ObjectMapper as swagger does (you can see a link in the question). I don't know as I don't work with swagger much. But I think swagger is the main problem, why the contextresolver is not being called.
  • Paul Samsotha
    Paul Samsotha about 9 years
    After further testing, The annotation does work. Even if We have to use Swagger ObjectMapper, then already configure the time stamps as false for us. So this should work. For better help, I strongly suggest you provide a MCVE that demonstrates the problem.
  • Paul Samsotha
    Paul Samsotha about 9 years
    Look at is this way, If I told you to run my app, and all I gave you was what you have provided in your post, could you run it? I would create a new project with the minimal configurations, classes, dependencies needed to reproduce the problem, then provide us with all that information.
  • JAB
    JAB about 9 years
    Thanks a Lot Peeskillet , Like you said the annotations work @JsonDeserialize(using = LocalDateDeserializer.class) @JsonSerialize(using = LocalDateSerializer.class) ... Actually i was getting exception while Deserializing i.e. in LocalDateDeserializer , while debugging i found it . Exception was because of the above generated json mentioned in Comment 1 . And using ObjectMapperContextResolver ... this can be resolved ..
  • DrunkenPope
    DrunkenPope over 7 years
    The class com.fasterxml.jackson.datatype.jsr310.JSR310Module is deprecated as of version 2.5, recommended is using the newer com.fasterxml.jackson.datatype.jsr310.JavaTimeModule.
  • user3774109
    user3774109 over 7 years
    new com.fasterxml.jackson.datatype.jsr310.JSR310Module() for version 2.5.4 of Jackson. JavaTimeModule class doesn't exist in this version.
  • rewolf
    rewolf about 7 years
    Does this work for deserialization? or only serialization? Not having success with deserialization
  • rewolf
    rewolf about 7 years
    I had to explicitly declare the deserializer @JsonDeserialize(using= LocalDateDeserializer.class)
  • NeuroXc
    NeuroXc almost 7 years
    Those classes are included in jackson-datatype-jsr310. No need to manually define them in your project.
  • dave
    dave about 6 years
    This solution worked for me, using the serializers in jackson-datatype-jsr310.
  • ruhong
    ruhong almost 6 years
    This answer also works for LocalDateTime (jackson 2.9.5). 1 additional dependency required, so my build.sbt looks like: "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.9.5", "com.fasterxml.jackson.datatype" % "jackson-datatype-jsr310" % "2.9.5"
  • Kid101
    Kid101 over 5 years
    One thing I'd like to add is to pass date as "2018-12-07" instead of "2018-12-7" else you'll get an error.
  • Shadow Man
    Shadow Man over 5 years
    Correct, it works with yyyy-MM-dd (2 digit month and day) format, not yyyy-M-d (1 digit month or day) format.
  • Jian Chen
    Jian Chen over 5 years
    If you use serializers and deserializers in jackson-datatype-jsr310, better add @JsonFormat(shape = JsonFormat.Shape.STRING) to your field. Without this format, the value will be serialized as [year, month, day], although deserialization will work.
  • brt
    brt over 5 years
    Brilliant, far and away the easiest. FYI for anyone with lots of dependencies, I had to update some other libraries which incorporated jackson annotations.
  • fsakiyama
    fsakiyama about 5 years
    This answer is the closest i got to fix my problem. Serialization is working, but deserialization is failing because of the pattern I used with @JsonFormat i think (@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy_HH:mm:SS").
  • Joost Lambregts
    Joost Lambregts almost 5 years
    This pointed me in the right direction, Thank you! I would add that in spring-boot all you need to do is add the following to application.properties: spring.jackson.serialization.write-dates-as-timestamps= false
  • Baha
    Baha over 4 years
    @JsonFormat just for changing output data format. stackoverflow.com/a/53251526/816759 works perfect with @JsonFormat, @JsonDeserialize, @JsonSerialize
  • esfandia
    esfandia about 4 years
    In Spring Boot, once you add the JSR310 dependency, all you need to do is add spring.jackson.serialization.write-dates-as-timestamps=false to your application.properties, and it formats it in yyyy-MM-dd automatically. No need for @JsonFormat
  • Cuga
    Cuga almost 4 years
    This helped me. In my case, I needed to add the MAPPER.registerModule(new JavaTimeModule()); line. It let me format LocalDate objects as "2020-02-20" format. I didn't need the MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTA‌​MPS, false); line, for what I was looking for
  • Cristopher Van Paul
    Cristopher Van Paul almost 4 years
    If you have a failed deserialization, most likely is your ObjectMapper doesn't have JavaTimeModule registered. If your ObjectMapper instance is provided from spring/MessageConverter framework. They did some magic to wire them up. In other case, should registerModule to enable LocalDateDeserializerby default for all "LocalDate" in POJO
  • W0lfw00ds
    W0lfw00ds over 3 years
    This works. Also requires the dependency for jackson-datatype-jsr310
  • SGuru
    SGuru over 3 years
    You saved my weekend!
  • Abhijith Ravindran
    Abhijith Ravindran over 3 years
    Simplest solution.
  • Marco Tizzano
    Marco Tizzano almost 3 years
    I just had the same issue and this solution works perfectly. Thanks @slisnychyi
  • alex
    alex almost 3 years
    this answer doesn't work for me Jackson version 2.9.0
  • vikas tiwari
    vikas tiwari almost 3 years
    After looking at so many solution, this worked for me. For me date was in "yyyyMMdd" format and it worked like charm. Thanks
  • Paul Wasilewski
    Paul Wasilewski almost 3 years
    @vikastiwari, happy to hear that. And it's easy as pie :)
  • sxc731
    sxc731 almost 3 years
    Are you sure about "no additional dependencies"? I have Jackson 2.12.4 and cannot locate this annotation.
  • Tom Silverman
    Tom Silverman almost 3 years
    yes, ObjectMapper bean did the trick. Thanks!
  • Paul
    Paul over 2 years
    FYI The jackson-datatype-jsr310 dependency is already included if you use spring-boot-starter-web, but you do need to register the JavaTimeModule in the mapper before usage.
  • Tony Brand
    Tony Brand about 2 years
    I have the same issue but I couldn't solve it Here is the link : stackoverflow.com/questions/71393850/…
  • Tony Brand
    Tony Brand about 2 years
    I have the same issue but I couldn't solve it Here is the link : stackoverflow.com/questions/71393850/…