How to deserialize JS date using Jackson? - java

I'm getting a date string from ExtJS in the format:
"2011-04-08T09:00:00"
when i try to deserialize this date, it changes the timezone to Indian Standard Time (adds +5:30 to the time) . This is how i'm deserializing the date:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
getObjectMapper().getDeserializationConfig().setDateFormat(dateFormat);
Doing this also doesn't change the timezone. I still get the date in IST:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
getObjectMapper().getDeserializationConfig().setDateFormat(dateFormat);
How do I deserialize the date in the way in which it is coming without the hassles of Timezone?

I found a work around but with this I'll need to annotate each date's setter throughout the project. Is there a way in which I can specify the format while creating the ObjectMapper?
Here's what I did:
public class CustomJsonDateDeserializer extends JsonDeserializer<Date>
{
#Override
public Date deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
String date = jsonParser.getText();
try {
return format.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
And annotated each Date field's setter method with this:
#JsonDeserialize(using = CustomJsonDateDeserializer.class)

This works for me - i am using jackson 2.0.4
ObjectMapper objectMapper = new ObjectMapper();
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
objectMapper.setDateFormat(df);

There is a good blog about this topic:
http://www.baeldung.com/jackson-serialize-dates
Use #JsonFormat looks the most simple way.
public class Event {
public String name;
#JsonFormat
(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
public Date eventDate;
}

In addition to Varun Achar's answer, this is the Java 8 variant I came up with, that uses java.time.LocalDate and ZonedDateTime instead of the old java.util.Date classes.
public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {
#Override
public LocalDate deserialize(JsonParser jsonparser, DeserializationContext deserializationcontext) throws IOException {
String string = jsonparser.getText();
if(string.length() > 20) {
ZonedDateTime zonedDateTime = ZonedDateTime.parse(string);
return zonedDateTime.toLocalDate();
}
return LocalDate.parse(string);
}
}

#JsonFormat only work for standard format supported by the jackson version that you are using.
Ex :- compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd")) for jackson 2.8.6

Related

How to dynamically customise deserialiser for date format?

I am working on custom JSON deserialiser and have the below class
public class yyyy_MM_dd_DateDeserializer extends StdDeserializer <LocalDate> {
public yyyy_MM_dd_DateDeserializer() {
this(null);
}
public yyyy_MM_dd_DateDeserializer(Class t) {
super(t);
}
#Override
public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
String dateString = jsonParser.getText();
LocalDate localDate = null;
try {
localDate = LocalDate.parse(dateString, "yyyy-MM-dd");
} catch (DateTimeParseException ex) {
throw new RuntimeException("Unparsable date: " + dateString);
}
return localDate;
}
}
and in my request class
#Valid
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonSerialize(using = LocalDateSerializer.class)
private LocalDate endDate;
It works fine but I am wondering if I can pass the date format dynamically. Instead of hardcoding in yyyy_MM_dd_DateDeserializer.
I want to pass the date format from my request class so that my deserialiser is more generic any anyone can use it by sending the required format.
I think you working too hard to get what you want. There is a simpler way without writing your own deserializer. Look at this question. Essentially it looks like
#JsonFormat(shape= JsonFormat.Shape.STRING, pattern="EEE MMM dd HH:mm:ss Z yyyy")
#JsonProperty("created_at")
ZonedDateTime created_at;
And you just put your own mask. Also, I once had a task of parsing date with unknown format, essentially I needed to parse any valid date. Here is an article describing the idea of how to implement it: Java 8 java.time package: parsing any string to date. You might find it useful
Not when using a binder library (The very point of binding is that it is not dynamic.).
But you could when using a simple parsing library such as org.json
When you are working with java.time.* classes and Jackson is good to start from registering JavaTimeModule which comes from jackson-datatype-jsr310 module. We can extend it and register serialiser with provided pattern like in below example:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
public class JsonApp {
public static void main(String[] args) throws Exception {
ObjectMapper mapperIso = createObjectMapper("yyyy-MM-dd");
ObjectMapper mapperCustom0 = createObjectMapper("yyyy/MM/dd");
ObjectMapper mapperCustom1 = createObjectMapper("MM-dd-yyyy");
System.out.println(mapperIso.writeValueAsString(new Time()));
System.out.println(mapperCustom0.writeValueAsString(new Time()));
System.out.println(mapperCustom1.writeValueAsString(new Time()));
}
private static ObjectMapper createObjectMapper(String pattern) {
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(pattern)));
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(javaTimeModule);
return mapper;
}
}
class Time {
private LocalDate now = LocalDate.now();
public LocalDate getNow() {
return now;
}
public void setNow(LocalDate now) {
this.now = now;
}
#Override
public String toString() {
return "Time{" +
"now=" + now +
'}';
}
}
Aboce code prints:
{"now":"2019-02-24"}
{"now":"2019/02/24"}
{"now":"02-24-2019"}

JPA #Past Date/Calendar validation

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).

Can not construct instance of java.util.Date from String value - not a valid representation

Sorry for what it seems to be a redundant question but i do have many options,
and despite my effort reading dozen of threads, i'm not able to understand what to do.
I do have a java application which job is to :
get datas from a SOAP WS (XML),
do some logic ( incoming deliverydate will become datebl )
Finally send it to a REST WS (JSON Format), which will update an Oracle Table
Plan is thus as follows :
SOAP WS ---- deliverydate ----> JAVA APP (logic) ---- datebl ----> REST WS
Jar used in JAVA APP are jackson-core-asl-1.9.13.jar and jackson-mapper-asl-1.9.13.jar among others.
Issues i have is when dealing with dates.
Readings :
( Tried to be inspired but jackson version seems not to be the same ) :
JSON Serializing date in a custom format (Can not construct instance of java.util.Date from String value)
Jackson 2.3.2: Issue with deserializing a Date despite of setting the date format to ObjectMapper
Edit 01/04/15
http://jackson-users.ning.com/forum/topics/cannot-deserialize-a-date
The facts now
Point 1 :
When recovering data from SOAP WS, deliverydate is a String which exact value is :
2014-07-31 07:00:00.0
Point 2:
Just before using the setter, i thought it could be a good idea to format this String to a date.
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date dateebl = dateFormat.parse(deliverydate);
msi.setDatebl(dateebl);
datebl declaration in POJO
private java.util.Date datebl;
At this stage, datebl value has transformed to
Thu Jul 31 07:00:00 CEST 2014
(despite choosing the specific format yyyy-MM-dd HH:mm:ss)
Point 3 and ERROR i have :
The error i have is thrown by the rest server:
Can not construct instance of java.util.Date from String value 'Thu
Jul 31 07:00:00 CEST 2014': not a valid representation (error: Can not
parse date "Thu Jul 31 07:00:00 CEST 2014": not compatible with any of
standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz",
"yyyy-MM-dd")) at [Source:
org.glassfish.jersey.message.internal.EntityInputStream#5709779; line:
1, column: 74] (through reference chain:
com.rest.entities.MvtSwapsIn["datebl"])
What i tried to do :
To resolve this, as i'm using a version prior to 2.x, i thought that my best option was to use a custom serializer, so :
In pojo, annotation was added just before the getter
#JsonSerialize(using = CustomJsonDateSerializer.class)
public java.util.Date getDatebl() {
return datebl;
}
Serializer was created as follows
public class CustomJsonDateSerializer extends JsonSerializer<Date> {
#Override
public void serialize(Date value, JsonGenerator jgen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
SimpleDateFormat dateFormat = new SimpleDateFormat(Properties.General.FORMAT_HEURE_JSON_SERIALIZE_2);
String dateString = dateFormat.format(value);
jgen.writeString(dateString);
}
}
In example,tried with FORMAT_HEURE_JSON_SERIALIZE_2, but tried many others without success.
public static final String FORMAT_HEURE_JSON = new String("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
public static final String FORMAT_HEURE_JSON_SERIALIZE = new String("EEE yyyy-MM-dd'T'HH:mm:ss.SSSZ");
public static final String FORMAT_HEURE_JSON_SERIALIZE_2 = new String("EEE, dd MMM yyyy HH:mm:ss zzz");
public static final String FORMAT_HEURE_JSON_SERIALIZE_3 = new String("yyyy-MM-dd HH:mm:ss");
At this point, i'm lost.
I don't know where and how to update my code.
Should i still use SimpleDateFormat after getting date from SOAP ws ?
Given the fact i'm using jackson 1.9, is my #JsonSerialize annotation good ? ( as well as the serializer ?)
Do i have to modify something on the rest server ?
Please can someone help me organize my thoughts ?
Kind regards,
Pierre
Open your POJO, annotate the date field declaration like so
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "<your_date_pattern>")
Where your_date_pattern can be something like
yyyy-MM-dd HH:mm:ss
Done
I guess you are using Jersey as a JAX-RS implementation. Have you left some details out from the stacktrace? Reading the stacktrace it seems the Jersey receives a String instead of a Date: Can not construct instance of java.util.Date from String value 'Thu Jul 31 07:00:00 CEST 2014'. If your class com.rest.entities.MvtSwapsIn["datebl"]) declares a date, this behaviour is a bit strange.
Anyway, for Jackson to work, one suggestion is to register a Jackson ObjectMapper to the REST config with a ContextResolver (this applies to both Jackson 1.x and 2.x). Try putting a breakpoint in both the constructor and getContext()-method to see if they are invoked at runtime:
public class ObjectMapperConfigContextResolver implements ContextResolver<ObjectMapper> {
ObjectMapper mapper;
public ObjectMapperConfigContextResolver() {
mapper.setDateFormat(new SimpleDateFormat("<your pattern>"));
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
The #Provider annotation should make JAX-RS pick up your configured ObjectMapper. If no you can do it manually:
#ApplicationPath("/")
public class RestApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(ObjectMapperConfigContextResolver.class);
return classes;
}
}
It would be helpful if you provided some information from your pom.xml (if you are using Maven).
Thanks a lot Jim, Jon.
Thomas.
Even if i found a workaround, i will check what you wrote carefully.
Nevertheless,i finally managed to solve this.
As i was not able to understand if problem was on Serialization(JavaApp) i've decided to have a look on the other side Deserialization(REST WS).
Quick reminder :
SOAP WS ----> JAVA APP ----> REST WS
Date received from SOAP WS was exactly received as String, its value was
2014-07-31 07:00:00.0
Within JAVA App, i did
DateFormat originalFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.S");
DateFormat targetFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = originalFormat.parse(deliverydate);
String formattedDate = targetFormat.format(date);
msi.setDatebl(date);
At this point, i had still the same error until i saw that JSON sent to REST WS was
{"id":87434958,"datebl":"Thu Jul 31 07:00:00 CEST 2014","price":15.45,"model":"AAA"}
*Some parts of JSON Object were cutted.
Within REST WS, i've added the following to the pojo
(Probably one of them is necessary) and created a custom deserializer as follows :
#JsonDeserialize(using = CustomJsonDateDeserializer.class)
private java.util.Date datebl;
#JsonDeserialize(using = CustomJsonDateDeserializer.class)
public void setDatebl(java.util.Date datebl) {
this.datebl = datebl;
}
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.DeserializationContext;
import org.codehaus.jackson.map.JsonDeserializer;
public class CustomJsonDateDeserializer extends JsonDeserializer<Date>
{
#Override
public Date deserialize(JsonParser jsonparser,
DeserializationContext deserializationcontext) throws IOException, JsonProcessingException {
DateFormat formatter = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy", Locale.US);
String dateStr = jsonparser.getText();
try {
return (Date)formatter.parse(dateStr);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
At this point, update has been done properly. Date was found in database in a proper format.
Readings :
How to convert "Mon Jun 18 00:00:00 IST 2012" to 18/06/2012?

JSON serialization strategy for dates

The problem I am having is that I have some consumers that are Java and some that are browsers. My target browsers are IE7+ (json3 for IE7 only) & Chrome.
For a browser I wish to have the date deserialize to a Date JavaScript object (using the JSON.parse() method. For a Java consumer I wish to deserialize to a java.util.Date Java object.
Given that I can't change anything on the browser side. I have to do serialize the messages to something like this:
{ myDate: new Date(<EPOCH HERE>) }
Which of course will cause a problem for Java deserializer. However, I am hoping there is something I can do with Gson to make this work...amy ideas?
Or should I take a different strategy altogether?
I usually use the annotation #JsonSerialize and #JsonDeserialize to deal with this problem. I also use ISO8601 format as a standard for our REST API dates.
#JsonSerialize(using=JsonDateSerializer.class)
#JsonDeserialize(using=JsonDateDeserializer.class)
private Date expiryDate;
JsonDateSerializer class
#Component
public class JsonDateSerializer extends JsonSerializer<Date>
{
// ISO 8601
private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
#Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonProcessingException
{
String formattedDate = dateFormat.format(date);
gen.writeString(formattedDate);
}
}
JsonDateDeserializer class
#Component
public class JsonDateDeserializer extends JsonDeserializer<Date>
{
// ISO 8601
private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException, JsonProcessingException
{
try
{
return dateFormat.parse(jsonParser.getText());
}
catch (ParseException e)
{
throw new JsonParseException("Could not parse date", jsonParser.getCurrentLocation(), e);
}
}
}

Date format Mapping to JSON 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.

Categories

Resources