Format date in response JSON from controller - java

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}" />

Related

Deserialize part of JSON string to DateTime in POJO using Jackson

I am reading a json of the given form and storing it as a POJO.
{
"details" : [
{
"version" : 1,
"time" : "2021-01-01T00:00:00.000Z",
}
]
}
My POJO class looks like :
public class Details
{
private int version;
private String time;
public Integer getVersion(){
return version;
}
public void setVersion(int version){
this.version = version;
}
public String getTime(){
return time;
}
public void setTime(String time){
this.time = time;
}
}
The time is being read as a string. How do I deserialize it to DateTime using Jackson?
Should be able to use #JsonFormat annotation for your date. First change your time field from String to Date then do the following:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy'T'hh:mm:ss.SSS'Z'")
private Date time;
The following link shows how to do other different conversions especially if its a standard time format
https://www.baeldung.com/jackson-serialize-dates
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
Adding this worked for me.
In POJO, gave time as 'DateTime' instead of 'String'.
public class Details
{
private int version;
private DateTime time;
...
//getters & setters
}

Unable to Deserialize - Jackson LocalDate/Time - JUnit

I need to deserialize the Json to Java Objects in Junit. I have Json file like
{
"studentId":57,
"JoinedDate":"31-12-2019",
"DOB":"08-06-1998"
}
I have class for the same to map
public class Student{
private long studentId ;
private LocalDate JoinedDate;
private LocalDate DOB ;
public long getStudentId() {
return studentId;
}
public void setStudentId(long studentId) {
this.studentId = studentId;
}
public LocalDate getJoinedDate() {
return JoinedDate;
}
public void setJoinedDate(LocalDate joinedDate) {
JoinedDate = joinedDate;
}
public LocalDate getDOB() {
return DOB;
}
public void setDOB(LocalDate dOB) {
DOB = dOB;
}
I need to write centralized builder for Unit testing project similar like this
builder.deserializers(new LocalDateDeserializer(DateTimeFormatter.ofPattern(dateFormat)));
builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern(dateFormat)));
Main Class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Main.class)
#WebAppConfiguration
public class Main{
#Test
public void contextLoads() {
assertTrue(true);
}
}
Unit testing Project looks like
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Main.class)
#WebAppConfiguration
public class StudentTest{
private ObjectMapper jsonObjectMapper;
#Before
public void setUp() throws IOException {
jsonObjectMapper = new ObjectMapper();
studentJson = IOUtils.toString(getClass().getResourceAsStream(CommonTestConstants.StudentPath+ "/Student.json"));
}
I'm getting a error while mapping the objects -
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type java.time.LocalDate from String "31-12-2019": Failed to deserialize java.time.LocalDate:
Another Error - Sometimes.
com.fasterxml.jackson.databind.JsonMappingException: Text '31-12-2019'
could not be parsed at index 0
I assume LocalDate format mismatch is the issue. Any suggestion to make it centralized way instead of specifying the format above the fields. Any one please advise?
Reference - Spring Boot JacksonTester custom serializer not registered
You just need to specify the date format by default jackson allows format of yyyy-MM-dd
public class Student{
private long studentId ;
#JsonProperty("JoinedDate") #JsonFormat(pattern = "dd/MM/yyyy")
private LocalDate JoinedDate;
#JsonProperty("DOB") #JsonFormat(pattern = "dd/MM/yyyy")
private LocalDate DOB ;
public long getStudentId() {
return studentId;
}
public void setStudentId(long studentId) {
this.studentId = studentId;
}
public LocalDate getJoinedDate() {
return JoinedDate;
}
public void setJoinedDate(LocalDate joinedDate) {
this.JoinedDate = joinedDate;
}
public LocalDate getDOB() {
return DOB;
}
public void setDOB(LocalDate dOB) {
this.DOB = dOB;
}
I hope it helps you
Springboot 1.4.x or above has this interface Jackson2ObjectMapperBuilderCustomizer which allows you to initialize objectMapper.
What we need to do, is override customize method and register deserializers and
serializers.
#SpringBootApplication
public class TestApplication implements Jackson2ObjectMapperBuilderCustomizer {
#Override
public void customize(Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
// pattern could be anything whatever is required
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/dd/MM");
LocalDateSerializer localDateDeserializer = new LocalDateSerializer(formatter);
jackson2ObjectMapperBuilder
.failOnEmptyBeans(false)
.deserializersByType(new HashMap<Class<?>, JsonDeserializer<?>>(){{
put(LocalTime.class, localTimeSerializer);
}});
}
}
We can also add seriliazers similar way.
jackson2ObjectMapperBuilder
.failOnEmptyBeans(false)
.serializersByType(new HashMap<Class<?>, JsonSerializer<?>>(){{
put(LocalTime.class, localTimeSerializer);
}});
you can check more details here. Spring Jackson builder

Problem with formatting LocalDateTime with org.json library

I have a problem with the serialization of an object to JSON using the org.json library.
In my code I have:
String resultStr = new JSONObject(result).toString();
and in result object two fields of type LocalDateTime:
private LocalDateTime startDate;
private LocalDateTime stopDate;
In variable resultStr I got date in following format:
2020-01-23T14:13:30.121205
I want this ISO format:
2016-07-14T07:58:08.158Z
I know that in Jackson there is an annotation #JsonFormat, but I didn't find anything like that in org.json. How to define a format of LocalDateTime in JSON string with org.json?
In JSON in Java, it seems that there are not much support for Date/Time formatting.
To customize the formatting of LocalDateTime field, we can make use of
1. #JSONPropertyIgnore to ignore the original getter to be serialized
2. #JSONPropertyName to annotate a new getter with ignored field name, which return the desired formatted date string, as following:
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.json.JSONObject;
import org.json.JSONPropertyIgnore;
import org.json.JSONPropertyName;
public class CustomizeLocalDateTimeFormatInOrgJson {
public static void main(String[] args) {
Result result = new Result(LocalDateTime.now(), LocalDateTime.now());
String resultStr = new JSONObject(result).toString();
System.out.println(resultStr);
}
public static class Result {
DateTimeFormatter customDateTimeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssS'Z'");
private LocalDateTime startDate;
#JSONPropertyIgnore
public LocalDateTime getStartDate() {
return startDate;
}
#JSONPropertyName("startDate")
public String getStartDateString() {
return customDateTimeFormat.format(startDate);
}
private LocalDateTime stopDate;
#JSONPropertyIgnore
public LocalDateTime getStopDate() {
return stopDate;
}
#JSONPropertyName("stopDate")
public String getStopDateString() {
return customDateTimeFormat.format(stopDate);
}
public void setStopDate(LocalDateTime stopDate) {
this.stopDate = stopDate;
}
public void setStartDate(LocalDateTime startDate) {
this.startDate = startDate;
}
public Result(LocalDateTime startDate, LocalDateTime stopDate) {
super();
this.startDate = startDate;
this.stopDate = stopDate;
}
}
}

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

Java 303 / 349 start date before end date validation

Is there a better way of writing a Java validator which ensures that a start date is before an end date than writing a class level ConstraintValidator in the following manner:
// VALIDATOR IMPLEMENTATION
public class StartBeforeEndDateValidator implements ConstraintValidator<StartBeforeEndDateValid, Object> {
// cannot use LocalDate here...
private String start;
private String end;
#Override
public void initialize(final StartBeforeEndDateValid annotation) {
start = annotation.start();
end = annotation.end();
}
#Override
public boolean isValid(final Object bean, final ConstraintValidatorContext context) {
try {
final String startDateStr = BeanUtils.getProperty(bean, start);
final String endDateStr = BeanUtils.getProperty(bean, end);
final LocalDate startDate = new LocalDate(startDateStr);
final LocalDate endDate = new LocalDate(endDateStr);
return !startDate.isAfter(endDate);
} catch (final Exception e) {
return false;
}
}
}
// USAGE
#StartBeforeEndDateValid(start = "startDate", end = "endDate")
#Entity
public class MyBean {
#NotNull
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
private LocalDate startDate;
#Type(type = "org.jadira.usertype.dateandtime.joda.PersistentLocalDate")
private LocalDate endDate;
...
}
I don't really like the fact that I have to use reflection to extract the 2 date objects from the bean. Unfortunately afaik the validation spec does not specify a way to set only the values you want to validate from the bean.
One way would be to add an interface to MyBean
public interface StartEndDateable {
public LocalDate getStartDate();
public LocalDate getEndDate();
}
public class MyBean implements StartEndDatable {
...
Then you can set the generic type on ConstraintValidator to the new interface instead of Object.
public class StartBeforeEndDateValidator implements ConstraintValidator<StartBeforeEndDateValid, StartEndDatable> {
#Override
public void initialize(StartBeforeEndDateValid annotation) {
}
#Override
public boolean isValid(StartEndDatable bean, ConstraintValidatorContext context) {
final LocalDate startDate = bean.getStartDate();
final LocalDate endDate = bean.getEndDate();
return !startDate.isAfter(endDate);
}
}
Obviously any class you then want to validate with the start and end date will have to implement the StartEndDateable (Not the best name, I know, but I'm sure you can think of something better) and define the getStartDate and getEndDate methods.

Categories

Resources