Hibernate - Override output format of date when generating JSON - java

With a temporal in my entity defined like:
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "start_time", length = 19, nullable = false)
public Date getStartTime() {
return this.startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
And marshaling JSON out put like this (simplified):
#GET
#RestSecure
#Path("/list")
#Produces(MediaType.APPLICATION_JSON)
public Response list(){
return Response.status(Response.Status.OK).entity(myEntityList).build();
}
Is there a simple way of overriding output date format?
What I am getting out is the epoch like this:
"startTime": 1582261711000,
What I need is the date in ISO 8601 format like this:
"startTime": "2020-02-21T05:08:31Z",

You can use jackson's DateFormat annotation:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssZ")

Related

How do I use different date formats for serialization & deserialization?

I'm getting an object with the date field in the format 2022-02-11 which I'm mapping into an object as follows:
{ "dateTimeField": "2022-02-11" }
#Data
class MyPojo {
#JsonFormat(pattern = "yyyy-MM-dd")
private Date dateTimeField;
}
Now I need to send this object as a json response, but I need it to be in a different format:
{ "dateTimeField": "2022-02-11 00:00:00" }
If I change the pattern field, deserialization fails:
#Data
class MyPojo {
// com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.util.Date` from String "2022-02-11": expected format "yyyy-MM-dd HH:mm:ss"
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date dateTimeField;
}
How do I use different patterns for serialization & deserialization?
This can be accomplished using separate annotations on the getter and setter of the field:
#Data
class MyPojo {
private Date dateTimeField;
// Used during serialization
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
public Date getDateTimeField() {
return dateTimeField;
}
// Used during deserialization
#JsonFormat(pattern = "yyyy-MM-dd")
public void setDateTimeField(Date dateTimeField) {
this.dateTimeField = dateTimeField;
}
}
Alternatively, using Lombok's (experimental as of 11 Feb 2022) onX feature:
#Data
class MyPojo {
#Getter(onMethod_ = {#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")}) // Used during serialization
#Setter(onMethod_ = {#JsonFormat(pattern = "yyyy-MM-dd")}) // Used during deserialization
private Date dateTimeField;
}

Format date in response JSON from controller

I am returning a JSON as a response from the controller. I want to format the date fields in this response.
Controller-
#RequestMapping(value = "/call", method = RequestMethod.GET)
public SampleDTO get()
{
......
return sampleDTO;
}
SampleDTO-
{
"date" : "2020-03-10T08:57:58+0000",
"text" : "abc"
}
I want to format the date field to dd-MM-yyyy
To do this I add the #JsonFormat annotation to the bean class of SampleDTO.
SampleDTO.java -
import java.util.Date;
public class SampleDTO
{
#JsonFormat(pattern = "dd-MM-yyyy")
private Date date;
private String text;
#JsonFormat(pattern = "dd-MM-yyyy")
public void setDate(final Date date)
{
this.date = date;
}
#JsonFormat(pattern = "dd-MM-yyyy")
public Date getDate()
{
return date;
}
public void setText(final String text)
{
this.text = text;
}
public String getText()
{
return text;
}
}
Still, I am getting this format in the response on my browser.
"date" : "2020-03-10T08:57:58+0000"
EDIT 1:
Instead of returning the sampleDTO, converting it to String directly in the code works perfectly fine.
This works like a charm:
SampleDTO sampleDTO = new SampleDTO();
sampleDTO.setCreated(new Date());
ObjectMapper om = new ObjectMapper();
return om.writeValueAsString(sampleDTO);
Please, check that your Date is from java.util and not from java.sql package. Plus try the following:
#JsonSerialize(as = Date.class)
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd-MM-yyyy")
Could you try this on the field level and remove from getDate() method in your DTO.
Something like this,
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
private Date date;
This should work with your current version of jackson-databind:2.9.8.jar.
Here is the small example for you:
public class ExampleMain {
public static void main(String[] args) throws IOException {
Employee employee = new Employee();
employee.setDateOfBirth(Date.from(ZonedDateTime.now().minusYears(30).toInstant()));
System.out.println("-- before serialization --");
System.out.println(employee);
System.out.println("-- after serialization --");
ObjectMapper om = new ObjectMapper();
String jsonString = om.writeValueAsString(employee);
System.out.println(jsonString);
System.out.println("-- after deserialization --");
System.out.println(om.readValue(jsonString, Employee.class));
}
}
public class Employee {
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy")
private Date dateOfBirth;
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
#Override
public String toString() {
return "Employee{" +
", dateOfBirth=" + dateOfBirth +
'}';
}
}
There are three levels of how you can solve this date format issue with Spring.
1) Using #JsonFormat on your date fields
In this case, you need to use the same annotation in front of all your private date members.
public class MyBean{
#JsonFormat(pattern="yyyy-MM-dd")
private Date birthday;
#JsonFormat(pattern="yyyy-MM-dd")
private LocalDate birthday;
// getters and setters here
}
2) Setting the Default format
If you want to configure the default date format for all dates in your application, add the following line to the application.properties or application.yml config file:
spring.jackson.date-format=yyyy-MM-dd
Unfortunately, this solution doesn't work with the Java 8 date types, like LocalDate and LocalDateTime.
3) Customizing your Jackson ObjectMapper
This solution works like a charm with Java 8 date types as well.
#Configuration
public class ContactAppConfig {
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
#Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> {
builder.simpleDateFormat(DATE_TIME_FORMAT);
builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)));
builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
};
}
}
I suggest you use the 3rd option.
you can use jstl format to format the date :)
<%# taglib prefix = "fmt" uri = "http://java.sun.com/jsp/jstl/fmt" %>
<fmt:formatDate pattern = "yyyy-MM-dd" value = "${date}" />

Validate that Java LocalDate matches yyyy-MM-dd format with readable message

I have below property in POJO class for DoB.
#NotNull(message = "dateOfBirth is required")
#JsonDeserialize(using = LocalDateDeserializer.class)
LocalDate dateOfBirth;
How can I validate that
User is sending valid date format (accepting only YYYY-MM-DD)
If user enters incorrect date I want to send custom message or more readable message. Currently if user entered invalid date then application sends below long error -
JSON parse error: Cannot deserialize value of type `java.time.LocalDate` from String \"1984-33-12\": Failed to deserialize java.time.LocalDate:
(java.time.format.DateTimeParseException) Text '1984-33-12' could not be parsed: Invalid value for MonthOfYear (valid values 1 - 12): 33;
...
You can use this annotation:
#JsonFormat(pattern = "YYYY-MM-DD")
You can read further about custom error messages when validating date format in here:
custom error message
You should create your custom deserializer, overwrite deserialize method to throw your custom error and use it in #JsonDeserialize
public class CustomDateDeserializer
extends StdDeserializer<LocalDate> {
private static DateTimeFormatter formatter
= DateTimeFormatter.ofPattern("YYYY-MM-DD");
public CustomDateDeserializer() {
this(null);
}
public CustomDateDeserializer(Class<?> vc) {
super(vc);
}
#Override
public LocalDate deserialize(
JsonParser jsonparser, DeserializationContext context)
throws IOException {
String date = jsonparser.getText();
try {
return LocalDate.parse(date, formatter);
} catch (DateTimeParseException e) {
throw new RuntimeException("Your custom exception");
}
}
}
Use it:
#JsonDeserialize(using = CustomDateDeserializer.class)
LocalDate dateOfBirth;
Something like this.
#Column(name = "date_of_birth")
#DateTimeFormat(iso = DateTimeFormatter.ISO_LOCAL_DATE)
#JsonFormat(pattern = "YYYY-MM-dd")
private LocalDateTime dateOfBirth;
DateTimeFormatter Java doc
https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html

Jackson #JsonFormat converting date with incorrect timezone

I have a value coming in from JSON payload as:
"callStartTime" : "2019-03-27 13:00:00"
Entity.java
#JsonProperty("callStartTime")
#Column(name = "call_start_dt", nullable = false)
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", lenient = OptBoolean.FALSE)
private Date callStartTime;
When I printed it on console, it says:
Wed Mar 27 08:00:00 CDT 2019
I wanted to be the same as it was in json payload. How I can fix it?
I am just taking date from json and writing it to mysql db in a datetime column.
Simple solution: I solved it by changing the data type to String which completes my aim to capture the value as it is coming from JSON payload. Using Date and other data types were converting the value to some different timezone.
#JsonProperty("callStartTime")
#Column(name = "call_start_dt", nullable = false)
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", lenient = OptBoolean.FALSE)
private **String** callStartTime;
Try this
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
private Date callStartTime;
java.util.Date does not capture time zone data. It only knows about number of millis since the epoch.
You could try using one of the modules in jackson-modules-java8 and deserialize into an instance of ZonedDateTime instead, which is time-zone aware.
EDIT: try this as a basis for getting it to work:
public class SoTest {
public static void main(String[] args) throws Exception {
ObjectMapper om = new ObjectMapper().registerModule(new ParameterNamesModule())
.registerModule(new JavaTimeModule());
String s = "{\"callStartTime\" : \"2019-03-27T13:00:00Z\" }";
MyType mt = om.readValue(s, MyType.class);
System.out.println(mt.getCallStartTime());
}
}
class MyType {
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ssX", lenient = OptBoolean.FALSE)
private ZonedDateTime callStartTime;
public ZonedDateTime getCallStartTime() {
return callStartTime;
}
public void setCallStartTime(ZonedDateTime date) {
this.callStartTime = date;
}
}
There are two possible solutions for this :
1. Define ObjectMapper bean and set date format.
#Bean
public ObjectMapper objectMapper()
{
ObjectMapper objectMapper = new ObjectMapper();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
objectMapper.setDateFormat(df);
return objectMapper;
}
2. Set date format for the particular field using #JsonFormat
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", lenient = OptBoolean.FALSE)
private Date createTime;

Converting date from timestamp to human readable in entity constructor

Currently, the format of the Date requestDate variable stored looks like: 2017-02-17 00:00:00.0. I want to convert this into, for example: Friday, February 17, 2017. I would like to do the conversion here in my entity and return it so that when it's displayed it is more human readable. This will likely happen in the constructor, at this line: this.setRequestDate(doDateConversion(requestDate));. How can I make this conversion?
My Request entity:
#Entity
#Table(name = "Request")
public class RequestDO implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="request_id")
private Long id;
private Date requestDate;
private String description;
private RequestStatus status;
/*private Boolean read;*/
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="user_id", nullable = false)
private Users users;
public RequestDO() {}
public RequestDO(Users user, Date requestDate) {
this.setUsers(user);
this.setRequestDate(requestDate);
}
#Override
public String toString() {
return String.format(
"RequestDO[id=%d, inital='%s', requestDate='%s']",
getId()
, getUsers().getInitialName()
, getRequestDate());
}
public Date getRequestDate() {
return requestDate;
}
public void setRequestDate(Date requestDate) {
this.requestDate = requestDate;
}
}
You can use SimpleDateFormat to convert your Date to a readable String of your choice.
The time format String for your example is EEEE, MMMM, dd, yyyy. You have to create a new SimpleDateFormat object and format your date to a String. Examples...
But Spring provides some specials out of the box. For example you can use Jackson for date format: #JsonFormat(pattern="yyyy-MM-dd") more. It is also possible to add a data format in application.properties file : spring.jackson.date-format
Using SimpleDateFormat:
java.sql.Date date = new Date(System.currentTimeMillis());
System.out.println(new SimpleDateFormat("EEEE, MMMM dd, YYYY").format(date));
See this for more details.
I solved the problem by changing the dates as they are read in my controller, using SimpleDateFormat:
#RequestMapping(value = "/requests", method = RequestMethod.GET)
public String getAllRequests(Model model, RequestModel requestModel) throws ParseException {
List<RequestDO> requestDOArrayList = new ArrayList<RequestDO>();
for (RequestDO requestDO : requestRepository.findAll()) {
log.info(requestDO.toString());
// Display all dates in Requests list in human-readable form
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse(requestDO.getRequestDate().toString());
log.info(String.valueOf(date));
requestDO.setRequestDate(date);
requestDOArrayList.add(requestDO);
}
model.addAttribute("requests", requestDOArrayList);
log.info(requestDOArrayList.toString());
return "requests";
}

Categories

Resources