Throw custom exception while deserializing the Date field using jackson in java - java

DTO:
#Getter
#Setter
#ToString
public class TestDto {
#NotNull
private String id;
#NotNull
#DateTimeFormat(pattern = "YYYY-MM-DD'T'hh:mm:ss.SSSZ")
private Instant timestamp;
}
When I give this input
{"timestamp":"4/23/2018 11:32 PM","id":"132"}
It gives BAD_REQUEST (which it should), but I want to handle this malformed date and throw an exception with my custom exception.
How can I add this?

Since OP requested feature is not supported yet: https://github.com/FasterXML/jackson-annotations/issues/130
Trying to do the same thing with a bit longer approach by using custom deserializer for a field timestamp
Custom exception class:
import com.fasterxml.jackson.core.JsonProcessingException;
public class MyException extends JsonProcessingException {
public MyException(String message) {
super(message);
}
}
Custom Deserializer class:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
public class InstantDeserializer extends StdDeserializer<Instant> {
public InstantDeserializer() {
this(null);
}
public InstantDeserializer(Class<?> vc) {
super(vc);
}
private SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD'T'hh:mm:ss.SSS'Z'");
#Override
public Instant deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
Date date = null;
try {
date = sdf.parse(node.asText());
} catch (Exception e) {
throw new MyException("Instant field deserialization failed");
}
return date.toInstant();
}
}
Updated TestDto class:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.time.Instant;
#Getter
#Setter
#ToString
public class TestDto {
#NotNull
private String id;
#NotNull
#JsonDeserialize(using = InstantDeserializer.class)
#DateTimeFormat(pattern = "YYYY-MM-DD'T'hh:mm:ss.SSS'Z'")
private Instant timestamp;
}
Invalid Input request:
{"timestamp":"4/23/2018 11:32 PM","id":"132"}
Response:
{
"timestamp": 1552845180271,
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Instant field deserialization failed; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Instant field deserialization failed (through reference chain: TestDto[\"timestamp\"])"
}
Valid Input Request:
{"timestamp":"2018-04-23T11:32:22.213Z","id":"132"}
Response:
{
"id": "132",
"timestamp": {
"epochSecond": 1514700142,
"nano": 213000000
}
}
If you do not like the way timestamp field is getting deserialized and would like to change that, this SO post will be helpful.

Related

How to catch customize exeception in Date JsonDeserializer?

Implementaion: Im trying to show the customize exception DATE_FORMAT_EXCEPTION with a message of Date format is wrong if error. But even i input a wrong format the customize exception wont show but there is error 400 Bad Request and i use Exception which catch all the error.
import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import com.cartgatewayservice.RestModelException.DATE_FORMAT_EXCEPTION;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
public class DateDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
DateTimeFormatter localDateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String dateValue = p.getValueAsString();
try {
LocalDate localDate = LocalDate.parse(dateValue, localDateFormatter);
return convertToDateViaInstant(localDate);
} catch (Exception e) {
throw new DATE_FORMAT_EXCEPTION("Date format is wrong");
}
}
public Date convertToDateViaInstant(LocalDate dateToConvert) {
return Date.from(dateToConvert.atStartOfDay()
.atZone(ZoneId.systemDefault())
.toInstant());
}
}
import java.util.Date;
import com.cartgatewayservice.BodyConvertParameters.DateDeserializer;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.OptBoolean;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.springframework.format.annotation.DateTimeFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Builder
public class ProductEntity {
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
#JsonFormat(pattern = "yyyy-MM-dd", shape = JsonFormat.Shape.STRING, lenient = OptBoolean.FALSE)
#JsonDeserialize(using = DateDeserializer.class)
private Date productexpirationdate;
}
public class DATE_FORMAT_EXCEPTION extends RuntimeException {
private static final long serialVersionUID = 1L;
public DATE_FORMAT_EXCEPTION(String message) {
super(message);
}
}
#ControllerAdvice
public class RestAdviceException extends ResponseEntityExceptionHandler {
#ExceptionHandler(DATE_FORMAT_EXCEPTION.class)
public ResponseEntity<Object> handleFormatDateEntity(DATE_FORMAT_EXCEPTION ex, WebRequest request) {
return new ResponseEntity<Object>(new ERROR_DETAILS_EXCEPTION(ex.getMessage(), HttpStatus.BAD_REQUEST, LocalDateTime.now()),HttpStatus.NOT_FOUND);
..........
}
}

Jackson ignoring specific property but able to check was it availble

Is there a way to skip some properties on deserialization but at the same time knowing are they presented or not?
{
"id": 123,
"name": "My Name",
"picture": {
// a lot of properties that's not important for me
}
}
#JsonIgnoreProperties(ignoreUnknown=true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
private int id;
}
So, I ignoreUnknown is what I want as a default behavior because I don't want name field and all other fields that can exist. The value of picture fields also is not important. I just want to know was picture property available or not. How I can do that?
You can add a boolean property and custom deserializer which just reads given value and returns true. Jackson invokes custom deserializer only if property exists in payload.
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.io.File;
import java.io.IOException;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./src/main/resources/test.json");
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(jsonFile, User.class));
}
}
class PropertyExistsJsonDeserializer extends JsonDeserializer<Boolean> {
#Override
public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
p.readValueAsTree(); //consume value
return Boolean.TRUE;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
class User {
private int id;
#JsonDeserialize(using = PropertyExistsJsonDeserializer.class)
#JsonProperty("picture")
private boolean pictureAvailable;
//getters, setters, toString
}
Above code prints:
User{id=123, pictureAvailable=true}

Jackson Json deserialize date string to java date

I have to read date string from json and set it to java object.
I have used the following annotations on my variable.
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
#JsonInclude(JsonInclude.Include.NON_NULL)
public class RequestObject {
#JsonProperty("doj")
#JsonDeserialize(using = CustomDateDeserializer.class, as=Date.class)
private Date doj;
public Date getDoj() {
return doj;
}
public void setDoj(Date doj) {
this.doj = doj;
}
}
Below is my Custom DateDeserializer.
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
#SuppressWarnings("serial")
public class CustomDateDeserializer extends JsonDeserializer<Date>{
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String date = jsonParser.getText();
try{
return sdf.parse(date);
}catch(ParseException e){
throw new RuntimeException(e);
}
}
}
I am testing this using postman, below is my postman request.
{
"doj":"2017-12-27"
}
But I am getting the following error.
Caused by: java.lang.IllegalArgumentException: Invalid format: "2017-12-27" is too short
at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:945) ~[joda-time-2.9.1.jar:2.9.1]
at com.exxonmobil.ace.hybris.jaxb.DateAdapter.unmarshal(DateAdapter.java:45) ~[DateAdapter.class:?]
at com.exxonmobil.ace.hybris.jaxb.DateAdapter.unmarshal(DateAdapter.java:1) ~[DateAdapter.class:?]
at org.eclipse.persistence.internal.jaxb.XMLJavaTypeConverter.convertDataValueToObjectValue(XMLJavaTypeConverter.java:149) ~[org.eclipse.persistence.moxy-2.6.1.jar:?]
Could someone please let me know where I am going wrong.
Regards,
Farhan

Json Deserialize Date not working [duplicate]

This question already has answers here:
Parse json date string in android
(7 answers)
Closed 6 years ago.
In Json list i'm getting such a date: "lastModifiedDate":1459202400000". Serialize and Deserialize class they seem to be ok.
Entity class:
import org.codehaus.jackson.map.annotate.JsonDeserialize;
import org.codehaus.jackson.map.annotate.JsonSerialize;
import org.hibernate.validator.constraints.Email;
#Data
#ToString
#Entity
public class UserEntity implements Serializable {
#JsonSerialize(using=JsonDateSerializer.class)
#JsonDeserialize(using=JsonDateDeserializer.class)
#Column(name = "LastModifiedDate", columnDefinition="DATETIME", nullable = true, insertable = true, updatable = true)
private java.util.Date lastModifiedDate;
}
Deserializer class:
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonProcessingException;
import org.springframework.stereotype.Component;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.*;
#Component
public class JsonDateDeserializer extends JsonDeserializer<Date> {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
try {
return dateFormat.parse(jsonParser.getText());
} catch (Exception e) {
throw new JsonParseException("Could not parse date", jsonParser.getCurrentLocation(), e);
}
}
What's wrong?
}
You are not parsing the date correctly. Right now you receive a date in milliseconds, so you have first to create a date before formatting, i.e.:
dateFormat.format(new Date(Long.parseLong(jsonParser.getText())));
Or if you just want to create the Date object from it, just skip the format part.

How to deserialize date format in spring hibernate?

i am writing a rest services where i am getting response as in format "1448994600000" for date but i need it to give response in date,month,year format.
if i send data by giving 12-2-2015 format it give me error
The request sent by the client was syntactically incorrect
In response i get "12333333333" format,i need it to response with 12-2-2015
i have used below code to deserialize it,but its not working what is going wrong in my code,Please guide me.
import java.io.Serializable;
import java.sql.Date;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;
#JsonAutoDetect
#Entity
#Table(name = "DataValueTable")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class DataValueTable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "ID")
private long id;
#JsonSerialize(using=JsonDateSerializer.class)
#Column(name = "Time")
private Date time;
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
JsonDateSerializer.java
package com.beingjavaguys.model;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
import org.springframework.stereotype.Component;
/**
* Used to serialize Java.util.Date, which is not a common JSON
* type, so we have to create a custom serialize method;.
*
* #author Loiane Groner
* http://loianegroner.com (English)
* http://loiane.com (Portuguese)
*/
#Component
public class JsonDateSerializer extends JsonSerializer<Date>{
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
#Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonProcessingException {
String formattedDate = dateFormat.format(date);
gen.writeString(formattedDate);
}
}
method in my controller
/* Getting List of objects in Json format in Spring Restful Services */
#RequestMapping(value = "/list", method = RequestMethod.GET)
public #ResponseBody List getDatalist() {
List DataList = null;
try {
DataList = dataServices.getDataEntityList();
} catch (Exception e) {
e.printStackTrace();
}
return DataList;
}
update data
#RequestMapping(value = "/updateData", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
Status updateData(#RequestBody DataValueTable dataObject) {
try {
//response.addHeader("Access-Control-Allow-Origin","*");
dataServices.insertData(dataObject);
return new Status(1, "Data updated Successfully !");
} catch (Exception e) {
// e.printStackTrace();
return new Status(0, e.toString());
}
}
You can use implementation which has been introduced to java8
Import specific libraries
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
LocalDate localDate= LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
String stamp = localDate.format(formatter);
If you need conversion from Date, you would do it as following
Date input = new Date();
LocalDate localDate = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
And the output could be sth like 13-01-2016

Categories