Date format Mapping to JSON Jackson

536,998

Solution 1

What is the formatting I need to use to carry out conversion with Jackson? Is Date a good field type for this?

Date is a fine field type for this. You can make the JSON parse-able pretty easily by using ObjectMapper.setDateFormat:

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
myObjectMapper.setDateFormat(df);

In general, is there a way to process the variables before they get mapped to Object members by Jackson? Something like, changing the format, calculations, etc.

Yes. You have a few options, including implementing a custom JsonDeserializer, e.g. extending JsonDeserializer<Date>. This is a good start.

Solution 2

Since Jackson v2.0, you can use @JsonFormat annotation directly on Object members;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm a z")
private Date date;

Solution 3

Of course there is an automated way called serialization and deserialization and you can define it with specific annotations (@JsonSerialize,@JsonDeserialize) as mentioned by pb2q as well.

You can use both java.util.Date and java.util.Calendar ... and probably JodaTime as well.

The @JsonFormat annotations not worked for me as I wanted (it has adjusted the timezone to different value) during deserialization (the serialization worked perfect):

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "CET")

@JsonFormat(locale = "hu", shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Europe/Budapest")

You need to use custom serializer and custom deserializer instead of the @JsonFormat annotation if you want predicted result. I have found real good tutorial and solution here http://www.baeldung.com/jackson-serialize-dates

There are examples for Date fields but I needed for Calendar fields so here is my implementation:

The serializer class:

public class CustomCalendarSerializer extends JsonSerializer<Calendar> {

    public static final SimpleDateFormat FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    public static final Locale LOCALE_HUNGARIAN = new Locale("hu", "HU");
    public static final TimeZone LOCAL_TIME_ZONE = TimeZone.getTimeZone("Europe/Budapest");

    @Override
    public void serialize(Calendar value, JsonGenerator gen, SerializerProvider arg2)
            throws IOException, JsonProcessingException {
        if (value == null) {
            gen.writeNull();
        } else {
            gen.writeString(FORMATTER.format(value.getTime()));
        }
    }
}

The deserializer class:

public class CustomCalendarDeserializer extends JsonDeserializer<Calendar> {

    @Override
    public Calendar deserialize(JsonParser jsonparser, DeserializationContext context)
            throws IOException, JsonProcessingException {
        String dateAsString = jsonparser.getText();
        try {
            Date date = CustomCalendarSerializer.FORMATTER.parse(dateAsString);
            Calendar calendar = Calendar.getInstance(
                CustomCalendarSerializer.LOCAL_TIME_ZONE, 
                CustomCalendarSerializer.LOCALE_HUNGARIAN
            );
            calendar.setTime(date);
            return calendar;
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

and the usage of the above classes:

public class CalendarEntry {

    @JsonSerialize(using = CustomCalendarSerializer.class)
    @JsonDeserialize(using = CustomCalendarDeserializer.class)
    private Calendar calendar;

    // ... additional things ...
}

Using this implementation the execution of the serialization and deserialization process consecutively results the origin value.

Only using the @JsonFormat annotation the deserialization gives different result I think because of the library internal timezone default setup what you can not change with annotation parameters (that was my experience with Jackson library 2.5.3 and 2.6.3 version as well).

Solution 4

To add characters such as T and Z in your date

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
private Date currentTime;

output

{
    "currentTime": "2019-12-11T11:40:49Z"
}

Solution 5

Just a complete example for spring boot application with RFC3339 datetime format

package bj.demo;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;

import java.text.SimpleDateFormat;

/**
 * Created by [email protected] at 2018/5/4 10:22
 */
@SpringBootApplication
public class BarApp implements ApplicationListener<ApplicationReadyEvent> {

    public static void main(String[] args) {
        SpringApplication.run(BarApp.class, args);
    }

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"));
    }
}
Share:
536,998

Related videos on Youtube

Kevin Rave
Author by

Kevin Rave

Updated on April 25, 2020

Comments

  • Kevin Rave
    Kevin Rave about 4 years

    I have a Date format coming from API like this:

    "start_time": "2015-10-1 3:00 PM GMT+1:00"
    

    Which is YYYY-DD-MM HH:MM am/pm GMT timestamp. I am mapping this value to a Date variable in POJO. Obviously, its showing conversion error.

    I would like to know 2 things:

    1. What is the formatting I need to use to carry out conversion with Jackson? Is Date a good field type for this?
    2. In general, is there a way to process the variables before they get mapped to Object members by Jackson? Something like, changing the format, calculations, etc.
  • realPK
    realPK about 9 years
    If you want to include timezone: @JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone="GMT")
  • krishna Ram
    krishna Ram over 8 years
    Hi, Which jar have that annotation. I am using jackson-mapper-asl 1.9.10 version. I am not getting it
  • Olivier Lecrivain
    Olivier Lecrivain over 8 years
    @Ramki : jackson-annotations >= 2.0
  • Miklos Krivan
    Miklos Krivan over 8 years
    This annotation only works perfect in serialization phase but during deserialization the timezone and locale information not used at all. I have tried timezone="CET" and timezone "Europe/Budapest" with locale="hu" but neither works and cause strange time conversation on Calendar. Only the custom serialization with deserialization worked for me handling timezone as required. Here is the perfect tutorial how you need to use baeldung.com/jackson-serialize-dates
  • Miklos Krivan
    Miklos Krivan over 8 years
    I have got downvote for my answer yesterday. I worked a lot on this topic so I do not understand. May I have got some feedback to learn from it? I would appreciate some notes in case of downvote. This way we can learn more from each other.
  • Stuart
    Stuart over 8 years
    Great answer, thanks it really helped me! Minor suggestion - consider putting CustomCalendarSerializer and CustomCalendarDeserializer as static classes within an enclosing parent class. I think this would make the code a little nicer :)
  • John Scattergood
    John Scattergood over 8 years
    12 hour format is better if the format also includes the AM/PM designation: DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm a z");
  • ocodo
    ocodo about 8 years
    @Stuart - you should simply offer this reorganisation of the code as another answer, or propose an edit. Miklos may not have the time free to do it.
  • Adam Gent
    Adam Gent about 8 years
    I downvoted you (I hate downvoting but I just don't want people to use your answer). SimpleDateFormat is not threadsafe. It is 2016 (you answered in 2016). You should not be using SimpleDateFormat when there are numerous faster and threadsafe options. There is even an exact misuse of what you are proposing Q/A here: stackoverflow.com/questions/25680728/…
  • Adam Gent
    Adam Gent about 8 years
    @MiklosKrivan I have downvoted you for several reasons. You should know that SimpleDateFormat is not threadsafe and frankily you should use alternative libraries for date formatting (Joda, Commons-lang FastDateFormat etc). The other reason is the setting of the timezone and even locale. It is far more preferable to use GMT in serialization environment and let your client say which timezone it is in or even attach the preferred tz as a separate string. Set your server to be on GMT aka UTC. Jackson has builtin ISO format.
  • Miklos Krivan
    Miklos Krivan about 8 years
    Thx @AdamGent for your feedback. I understand and accept your suggestions. But in this particular case I just wanted to focus that the JsonFormat annotation with locale information is not working the way we would expect. And how can be solved.
  • Stuart
    Stuart about 8 years
    @AdamGent thanks for the pointing this out. In this context, using Jackson, the ObjectMapper class is thread safe so it doesn't matter. However, I do take your point that the code may be copied and used in a non-thread safe context. So I have edited my answer to make access to SimpleDateFormat thread safe. I also acknowledge there are alternatives, primarily the java.time package.
  • RedEagle
    RedEagle almost 8 years
    Tried all these solutions but was not able to store a Date variable of my POJO into a Map key value, also as Date. I want this to then instantiate a BasicDbObject (MongoDB API) from the Map, and consequently store the variable in a MongoDB DB collection as Date (not as Long or String). Is this even possible? Thank you
  • bub
    bub almost 8 years
    This is a separate jar project called Jackson Annotations. Sample pom entry
  • houcros
    houcros over 7 years
    Is it just as easy to use Java 8 LocalDateTime or ZonedDateTime instead of Date? Since Date is basically deprecated (or at least many of its methods), I would like to use those alternatives.
  • deFreitas
    deFreitas over 7 years
    @OlivierLecrivain this annotation is dangerous because your timezone is not the same of JVM, at least in my experience
  • MiguelMunoz
    MiguelMunoz over 6 years
    The javadocs for setSateFormat() say that this call makes the ObjectMapper no longer Thread safe. I created JsonSerializer and JsonDeserializer classes.
  • Iqbal
    Iqbal about 6 years
    This wasn't working for me until I added @JsonSerialize(as = Date.class) before @JsonFormat annotation.
  • brass monkey
    brass monkey over 5 years
    As the question does not mention java.util.Date explicitly I want to point out that this does not work for java.sql.Date. See also my answer below.
  • Ole V.V.
    Ole V.V. over 5 years
    Thanks for pointing out one consequence of this bad design of the two different Date classes. These days one shouldn’t any of them, though, nor SimpleDateFormat. java.time, the modern Java date and time API, has replaced them nearly 5 years ago. Use it and FasterXML/jackson-modules-java8.
  • Mohnish
    Mohnish over 5 years
    If you do not mean UTC time, you must specify the timezone. Jackson's default behavior is to assume UTC, it doesn't derive it from JVM's timezone. Ref: Java Docs
  • Alex
    Alex over 4 years
    don't forget to include jackson-datatype-jsr310 dependency in your pom, and register java time module as mapper.registerModule(new JavaTimeModule()); see stackoverflow.com/questions/21384820/…
  • Koray Tugay
    Koray Tugay over 3 years
    What is com.alibaba.fastjson.annotation.JSONField ?
  • Hammad Dar
    Hammad Dar almost 3 years
    It is old but SimpleDateFormat is not thread safe while ObjectMapper is and in most of the scenarios, ObjectMapper is re-used. This can create problems in date parsing, hence, serializer approach seems correct while this one incorrect.