I got the following piece of code:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String now = new ObjectMapper().writeValueAsString(new SomeClass(LocalDateTime.now()));
System.out.println(now);
And I get this:
{"time":{"hour":20,"minute":49,"second":42,"nano":99000000,"dayOfYear":19,"dayOfWeek":"THURSDAY","month":"JANUARY","dayOfMonth":19,"year":2017,"monthValue":1,"chronology":{"id":"ISO","calendarType":"iso8601"}}}
What I want to achieve is a string in ISO8601
2017-01-19T18:36:51Z
This is probably due to mistake in your code. You were using new unconfigured instance of mapper, here is the fix:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String now = mapper.writeValueAsString(new SomeClass(LocalDateTime.now()));
System.out.println(now);
This is what you can do for OffsetDateTime:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "uuuu-MM-dd'T'HH:mm:ss.SSSXXXX")
private OffsetDateTime timeOfBirth;
For LocalDateTime you cannot use XXXX (zone offset) because there is no offset information. So you can drop it. But ISO8601 discourages using Local Time as it's ambiguous:
If no UTC relation information is given with a time representation,
the time is assumed to be in local time. While it may be safe to
assume local time when communicating in the same time zone, it is
ambiguous when used in communicating across different time zones. Even
within a single geographic time zone, some local times will be
ambiguous if the region observes daylight saving time. It is usually
preferable to indicate a time zone (zone designator) using the
standard's notation.
I use SimpleModule and the best practice for me is to have my own registered implementation for serialization and deserialization:
final SimpleModule localDateTimeSerialization = new SimpleModule();
localDateTimeSerialization.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
localDateTimeSerialization.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
objectMapper.registerModule(localDateTimeSerialization);
Serializer:
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
private final DateTimeFormatter format = DateTimeFormatter.ISO_DATE_TIME;
#Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.format(format));
}
}
And deserialization:
public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
private final DateTimeFormatter fmt = DateTimeFormatter.ISO_DATE_TIME;
#Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext context) throws IOException {
return LocalDateTime.parse(p.getValueAsString(), fmt);
}
}
Related
Jackson's JavaTimeModule serialize/deserializejava.time well globally, but its default date-time format is ISO standard, like 2018-01-10T10:20:30 for LocalDateTime and 2018-01-10T10:20:30+08:00 for OffsetDateTime. But I need to set a global local format like 2018-01-10 10:20:30 for LocalDateTime and OffsetDateTime, without T and OffsetTime (use local default OffsetTime). How can I do this?
Notes: I know about #JsonFormat, #JsonSerialize and #JsonDeserialize. That is not global setting.
Spring boot
#SpringBootApplication
public class Application implements Jackson2ObjectMapperBuilderCustomizer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void customize(Jackson2ObjectMapperBuilder builder) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTimeSerializer localDateTimeSerializer = new LocalDateTimeSerializer(formatter);
builder.failOnEmptyBeans(false) // prevent InvalidDefinitionException Error
.serializerByType(LocalDateTime.class, localDateTimeSerializer);
}
}
Springboot & Spring Framework
#Configuration
public class WebMvcConfig implements WebMvcConfigurer {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTimeSerializer localDateTimeSerializer = new LocalDateTimeSerializer(formatter);
LocalDateTimeDeserializer localDateTimeDeserializer = new LocalDateTimeDeserializer(formatter);
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(LocalDateTime.class, localDateTimeSerializer);
module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
// add converter at the very front
// if there are same type mappers in converters, setting in first mapper is used.
converters.add(0, new MappingJackson2HttpMessageConverter(mapper));
}
}
hope this could help you.
If you are using a single instance of ObjectMapper globally, (and want a solution independent of Spring/Java 8 jackson modules,) you can do something like:
public ObjectMapper getCustomConfigMapper() {
final ObjectMapper mapper = new ObjectMapper();
final SimpleModule module = new SimpleModule();
module.addDeserializer(LocalDateTime.class, new CustomLocaDateTimeDeserializer());
mapper.registerModule(module);
return mapper;
}
public static class CustomLocaDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
public static final DateTimeFormatter CUSTOM_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
#Override
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext __) throws IOException {
final String value = jsonParser.getText().strip();
return LocalDateTime.parse(value, CUSTOM_FORMATTER);
}
}
You can configure an ObjectMapper. This page explains how https://www.baeldung.com/jackson-serialize-dates. I think you want something close to example 4 on that page.
Then you need to make that the global ObjectMapper. Different frameworks use different methods. This page explains how to do that in Spring and Spring-boot Configuring ObjectMapper in Spring. For others just Google for it.
In a spring-boot based project i have a simple DTO object:
public class ExpenseDTO {
#Min(value = 1, message = "expense.amount.negative")
private int amount;
#Past
private Calendar createdAt;
// setters/getters/constructor are omitted
}
and such rest controller:
public class ExpenseController {
private final ExpenseService expenseService;
#Autowired
public ExpenseController(ExpenseService expenseService) {
this.expenseService = expenseService;
}
#RequestMapping(value = ADD_EXPENSE, method = POST)
public ResponseEntity addExpense(#Valid #RequestBody ExpenseDTO expenseDTO, Principal principal) {
expenseService.addExpense(expenseDTO, principal.getName());
return ResponseEntity.ok().build();
}
}
From a client i'm gonna send a current date: {"createdAt": "2017-01-27T21:32:19.183Z"} but during validation on the back end the date will be parsed as "2017-01-28T01:30:00.000+0200" so the result is wrong and validation fails. I was trying to play around with #JsonFormat but with no result.
NOTE: i'm using H2 db and if remove #Past from the DTO object everything works just fine, but i have to disable future date.
So how can i validate the date without timezone, i mean i need the exactly the same date on the back end as it was send from a client?!
It might be because of the TimeZone set in your local machine. You can set it to UTC in the application startup, e.g.:
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
Also, you need to set the TimeZone in the SimpleDateFormat instance that is being configured inside ObjectMapper (if you are configuring ObjectMapper as a bean), e.g.:
#Bean
public ObjectMapper objectMapper(){
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.setSerializationInclusion(Include.NON_NULL);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
objectMapper.setDateFormat(simpleDateFormat);
return objectMapper;
}
If it still does not work, I would recommend creating a custom deserializer for Calendar and manually setting TimeZone in new instance, e.g.:
#Component
public class CalendarDeserialiser extends JsonDeserializer<Calendar>{
TimeZone UTC = TimeZone.getTimeZone("UTC");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSS'Z'");
#Override
public Calendar deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Calendar calendar;
try{
calendar = Calendar.getInstance(UTC);
calendar.setTime(dateFormat.parse(p.getText()));
}catch(Exception e){
throw new IOException(e);
}
calendar.setTimeInMillis(p.getLongValue());
return calendar;
}
}
And annotate your Calendar field with #JsonDeserialize(using = CalendarDeserialiser.class).
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:
What is the formatting I need to use to carry out conversion with Jackson? Is Date a good field type for this?
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.
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;
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.
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).
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"
}
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 BaiJiFeiLong#gmail.com 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"));
}
}
Building on #miklov-kriven's very helpful answer, I hope these two additional points of consideration prove helpful to someone:
(1) I find it a nice idea to include serializer and de-serializer as static inner classes in the same class. NB, using ThreadLocal for thread safety of SimpleDateFormat.
public class DateConverter {
private static final ThreadLocal<SimpleDateFormat> sdf =
ThreadLocal.<SimpleDateFormat>withInitial(
() -> {return new SimpleDateFormat("yyyy-MM-dd HH:mm a z");});
public static class Serialize extends JsonSerializer<Date> {
#Override
public void serialize(Date value, JsonGenerator jgen SerializerProvider provider) throws Exception {
if (value == null) {
jgen.writeNull();
}
else {
jgen.writeString(sdf.get().format(value));
}
}
}
public static class Deserialize extends JsonDeserializer<Date> {
#Overrride
public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws Exception {
String dateAsString = jp.getText();
try {
if (Strings.isNullOrEmpty(dateAsString)) {
return null;
}
else {
return new Date(sdf.get().parse(dateAsString).getTime());
}
}
catch (ParseException pe) {
throw new RuntimeException(pe);
}
}
}
}
(2) As an alternative to using #JsonSerialize and #JsonDeserialize annotations on each individual class member you could also consider overriding Jackson's default serialization by applying the custom serialization at an application level, that is all class members of type Date will be serialized by Jackson using this custom serialization without explicit annotation on each field. If you are using Spring Boot for example one way to do this would as follows:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public Module customModule() {
SimpleModule module = new SimpleModule();
module.addSerializer(Date.class, new DateConverter.Serialize());
module.addDeserializer(Date.class, new Dateconverter.Deserialize());
return module;
}
}
If anyone has problems with using a custom dateformat for java.sql.Date, this is the simplest solution:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(java.sql.Date.class, new DateSerializer());
mapper.registerModule(module);
(This SO-answer saved me a lot of trouble: https://stackoverflow.com/a/35212795/3149048 )
Jackson uses the SqlDateSerializer by default for java.sql.Date, but currently, this serializer doesn't take the dateformat into account, see this issue: https://github.com/FasterXML/jackson-databind/issues/1407 .
The workaround is to register a different serializer for java.sql.Date as shown in the code example.
I want to point out that setting a SimpleDateFormat like described in the other answer only works for a java.util.Date which I assume is meant in the question.
But for java.sql.Date the formatter does not work.
In my case it was not very obvious why the formatter did not work because in the model which should be serialized the field was in fact a java.utl.Date but the actual object ended up beeing a java.sql.Date.
This is possible because
public class java.sql extends java.util.Date
So this is actually valid
java.util.Date date = new java.sql.Date(1542381115815L);
So if you are wondering why your Date field is not correctly formatted make sure that the object is really a java.util.Date.
Here is also mentioned why handling java.sql.Date will not be added.
This would then be breaking change, and I don't think that is warranted. If we were starting from scratch I would agree with the change, but as things are not so much.
Working for me. SpringBoot.
import com.alibaba.fastjson.annotation.JSONField;
#JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
output:
{
"createTime": "2019-06-14 13:07:21"
}
If we are having the spring boot application, then one more option thats simple to implement for app wide configuration is to use below in application properties file. You can customize the format as needed.
spring.jackson.date-format=yyyy-MM-dd'T'HH:mm:ss.SSS
NOTE: If using this solution use the Spring Dependency Injection to get the reference of the ObjectMapper class.
Cons of not using explicit format is sometimes while upgrading the libraries for jackson code breaks because of change in the format for some versions.
I am using Spring WebMVC, JodaTime and Jackson to build a RESTful webservice.
Every user who performs actions on this webservice has his default timezone saved in the database.
I need to provide all timestamp in the users timezone. I am able to convert every timestamp in a response object to the correspondending timezone of the user, but jackson deserializes every timestamp to a specific timezone, for example UTC.
How do I prevent jackson from doing this? I want a datetime field to be serialized in its timezone, not the timezone set for jackson.
I am using full ISO6801 format.
Edit:
For anyone who stumbles upon this question, there is currently a discussion going on at Github about this topic:
https://github.com/FasterXML/jackson-datatype-joda/issues/43
You can consider customizing the standard Joda time deserializer to read the timezone information from a thread local variable set by every request.
Here is an example:
public class JacksonTimezone {
public static class DataTimeDeserializerTimeZone extends DateTimeDeserializer {
public static final ThreadLocal<DateTimeZone> TIME_ZONE_THREAD_LOCAL = new ThreadLocal<DateTimeZone>() {
#Override
protected DateTimeZone initialValue() {
return DateTimeZone.getDefault();
}
};
public DataTimeDeserializerTimeZone() {
super(DateTime.class);
}
#Override
public ReadableDateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
return super.deserialize(jp, ctxt).toDateTime().withZone(TIME_ZONE_THREAD_LOCAL.get());
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
JodaModule module = new JodaModule();
module.addDeserializer(DateTime.class,
(JsonDeserializer) new DataTimeDeserializerTimeZone());
mapper.registerModule(module);
DateTime dateTime1 = DateTime.parse("2014-02-03T10:00");
String json = mapper.writeValueAsString(dateTime1);
System.out.println(json + " " + TIME_ZONE_THREAD_LOCAL.get());
System.out.println(mapper.readValue(json, DateTime.class));
TIME_ZONE_THREAD_LOCAL.set(DateTimeZone.forID("US/Hawaii"));
System.out.println(mapper.readValue(json, DateTime.class));
}
}
Output:
1391418000000 Europe/Oslo
2014-02-03T10:00:00.000+01:00
2014-02-02T23:00:00.000-10:00
The value of the TIME_ZONE_THREAD_LOCAL static variable should be set to the correct timezone before JSON data reaches Jackson.
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:
What is the formatting I need to use to carry out conversion with Jackson? Is Date a good field type for this?
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.
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;
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.
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).
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"
}
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 BaiJiFeiLong#gmail.com 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"));
}
}
Building on #miklov-kriven's very helpful answer, I hope these two additional points of consideration prove helpful to someone:
(1) I find it a nice idea to include serializer and de-serializer as static inner classes in the same class. NB, using ThreadLocal for thread safety of SimpleDateFormat.
public class DateConverter {
private static final ThreadLocal<SimpleDateFormat> sdf =
ThreadLocal.<SimpleDateFormat>withInitial(
() -> {return new SimpleDateFormat("yyyy-MM-dd HH:mm a z");});
public static class Serialize extends JsonSerializer<Date> {
#Override
public void serialize(Date value, JsonGenerator jgen SerializerProvider provider) throws Exception {
if (value == null) {
jgen.writeNull();
}
else {
jgen.writeString(sdf.get().format(value));
}
}
}
public static class Deserialize extends JsonDeserializer<Date> {
#Overrride
public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws Exception {
String dateAsString = jp.getText();
try {
if (Strings.isNullOrEmpty(dateAsString)) {
return null;
}
else {
return new Date(sdf.get().parse(dateAsString).getTime());
}
}
catch (ParseException pe) {
throw new RuntimeException(pe);
}
}
}
}
(2) As an alternative to using #JsonSerialize and #JsonDeserialize annotations on each individual class member you could also consider overriding Jackson's default serialization by applying the custom serialization at an application level, that is all class members of type Date will be serialized by Jackson using this custom serialization without explicit annotation on each field. If you are using Spring Boot for example one way to do this would as follows:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public Module customModule() {
SimpleModule module = new SimpleModule();
module.addSerializer(Date.class, new DateConverter.Serialize());
module.addDeserializer(Date.class, new Dateconverter.Deserialize());
return module;
}
}
If anyone has problems with using a custom dateformat for java.sql.Date, this is the simplest solution:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(java.sql.Date.class, new DateSerializer());
mapper.registerModule(module);
(This SO-answer saved me a lot of trouble: https://stackoverflow.com/a/35212795/3149048 )
Jackson uses the SqlDateSerializer by default for java.sql.Date, but currently, this serializer doesn't take the dateformat into account, see this issue: https://github.com/FasterXML/jackson-databind/issues/1407 .
The workaround is to register a different serializer for java.sql.Date as shown in the code example.
I want to point out that setting a SimpleDateFormat like described in the other answer only works for a java.util.Date which I assume is meant in the question.
But for java.sql.Date the formatter does not work.
In my case it was not very obvious why the formatter did not work because in the model which should be serialized the field was in fact a java.utl.Date but the actual object ended up beeing a java.sql.Date.
This is possible because
public class java.sql extends java.util.Date
So this is actually valid
java.util.Date date = new java.sql.Date(1542381115815L);
So if you are wondering why your Date field is not correctly formatted make sure that the object is really a java.util.Date.
Here is also mentioned why handling java.sql.Date will not be added.
This would then be breaking change, and I don't think that is warranted. If we were starting from scratch I would agree with the change, but as things are not so much.
Working for me. SpringBoot.
import com.alibaba.fastjson.annotation.JSONField;
#JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
output:
{
"createTime": "2019-06-14 13:07:21"
}
If we are having the spring boot application, then one more option thats simple to implement for app wide configuration is to use below in application properties file. You can customize the format as needed.
spring.jackson.date-format=yyyy-MM-dd'T'HH:mm:ss.SSS
NOTE: If using this solution use the Spring Dependency Injection to get the reference of the ObjectMapper class.
Cons of not using explicit format is sometimes while upgrading the libraries for jackson code breaks because of change in the format for some versions.