I need to persist object in Azure Table Storage. Problem is I don't want to use Date type in my Entity class. I would like to use Instant but I have problems with parsing with the JasonMappingException.
Here is my entity class:
#Data
public class Event extends TableServiceEntity {
#NotNull
#JsonDeserialize(using = JsonDateDeserializer.class)
private Instant eventStart;
#NotNull
#JsonDeserialize(using = JsonDateDeserializer.class)
private Instant eventEnd;
#NotBlank
private String eventName;
private String eventDescription;
//default constructor
public Event () {}
}
And here is my deserializer:
public class JsonDateDeserializer extends JsonDeserializer<Instant> {
#Override
public Instant deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
String date = jsonParser.getText();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
try {
return sdf.parse(date).toInstant();
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
I am getting this error:
2020-06-10 20:47:19.685 WARN 11745 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: java.text.ParseException: Unparseable date: "2020-06-12T08:00:00UTC"; nested exception is com.fasterxml.jackson.databind.JsonMappingException: java.text.ParseException: Unparseable date: "2020-06-12T08:00:00UTC" (through reference chain: com.komix.eventmanager.model.Event["eventStart"])]
I am soo tired of the date problems with Azure DB. Could you please help me?
EDIT: Ok, after some edit parsing is fine but application gives me following error:
java.lang.IllegalArgumentException: Type class java.time.Instant is not supported.
It seems that Azure Table Storage doesn't support Instant??! I can't even find any good documentation for Asure Table data types supported...
'Z' is considered here as constants. You need to pass z without the quotes.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz");
Doc ref here
Related
I have a JSON String and I am using Jackson to parse it into a POJO. The incoming JSON string has a different format, so I had to write my own converter to convert the String to LocalTimeDate object. I use 2 annotations to achieve this.
public class Content {
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
#TDateFormat
private LocalDateTime modifiedDate;
}
TDateFormat: This annotation specifies the date format we need. It also has a default value.
#Retention(RetentionPolicy.RUNTIME)
public #interface TDateFormat {
String value() default "MMM d, yyyy, hh:mm:ss a";
}
LocalDateTimeDeserializer: This holds the logic to deserialize. We use the TDateFormat value get the format to use. I have extracted the logic out to an Util class
#NoArgsConstructor
public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> implements ContextualDeserializer {
private String format;
public LocalDateTimeDeserializer(String format) {
this.format = format;
}
#Override
public JsonDeserializer<?> createContextual(DeserializationContext dc, BeanProperty bp) {
return new LocalDateTimeDeserializer(bp.getAnnotation(TDateFormat.class).value());
}
#Override
public LocalDateTime deserialize(JsonParser jp, DeserializationContext dc) throws IOException {
LocalDateTime localDateTime = Utils.convertDate(jp.getText(), format);
System.out.println("Done::: " + localDateTime);
return localDateTime;
}
}
The above code works fine. Now, I am trying to combine #TDateFormat and #JsonDeserialize(using = LocalDateTimeDeserializer.class) to #TDeserializer
#TDeserializer Looks like below.
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
#TDateFormat
public #interface TDeserializer {
}
And I want to use the annotation like this
public class Content {
#TDeserializer
private LocalDateTime modifiedDate;
}
But I get the following error, when I make this little change.
2022-09-29 23:32:09.569 ERROR 90506 --- [ntainer#0-0-C-1] c.t.k.service.impl.KafkaMessageConsumer : Exception occurred::: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
at [Source: (String)"{"modifiedDate":"Sep 29, 2022, 11:25:56 PM" [truncated 149 chars]; line: 1, column: 52] (through reference chain: com.techopact.kafkautil.model.Article["modifiedDate"])
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
Can anyone please let me know what more I have to do to combine the annotations?
main() {
StudentDataDto source= new StudentDataDto();
studentDataDto.setCreatedAt("2022-01-20T11:12:46");
StudentMetaDataEntity destination= modelMapper.map(studentDataDto,
StudentMetaDataEntity.class);
}
StudentDataDto {
private String createdAt;
}
StudentMetaDataEntity {
private Timestamp createdAt; (java.sql.Timestamp)
}
Exception message:
org.modelmapper.MappingException: ModelMapper mapping errors:
1) Converter org.modelmapper.internal.converter.DateConverter#2b08772d failed to convert java.lang.String to java.sql.Timestamp.
Caused by: org.modelmapper.MappingException: ModelMapper mapping errors:
1) String must be in JDBC format [yyyy-MM-dd HH:mm:ss.fffffffff] to create a java.sql.Timestamp
1 error
at org.modelmapper.internal.Errors.toMappingException(Errors.java:258)
at org.modelmapper.internal.converter.DateConverter.dateFor(DateConverter.java:125)
at org.modelmapper.internal.converter.DateConverter.convert(DateConverter.java:70)
at org.modelmapper.internal.converter.DateConverter.convert(DateConverter.java:53)
at org.modelmapper.internal.MappingEngineImpl.convert(MappingEngineImpl.java:306)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:109)
at org.modelmapper.internal.MappingEngineImpl.setDestinationValue(MappingEngineImpl.java:245)
at org.modelmapper.internal.MappingEngineImpl.propertyMap(MappingEngineImpl.java:187)
at org.modelmapper.internal.MappingEngineImpl.typeMap(MappingEngineImpl.java:151)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:114)
at org.modelmapper.internal.MappingEngineImpl.map(MappingEngineImpl.java:71)
at org.modelmapper.ModelMapper.mapInternal(ModelMapper.java:573)
at org.modelmapper.ModelMapper.map(ModelMapper.java:406)
...
By referring to this similar question's answers, I understand that the source code of model mapper restrict the timestamp string formats.
My question:
Instead of changing my StudentDataDto property type to java.sql.Timestamp, is it possible that I keep my desired timestamp format in yyyy-MM-dd'T'HH:mm:ss and customize my modelmapper converter to solve the exception?
You just need to write you own converter and register it with ModelMapper instance.
Option 1 - more general. If your date string will always be in this format, you can write a converter from String to java.sql.Timestamp, so it will always be applied even when using other dtos.
public class StringToTimestampConverter implements Converter<String, Timestamp> {
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
#Override
public Timestamp convert(MappingContext<String, Timestamp> mappingContext) {
String source = mappingContext.getSource();
LocalDateTime dateTime = LocalDateTime.parse(source, this.formatter);
return Timestamp.valueOf(dateTime);
}
}
Basically convert string to LocaDateTime using DateTimeFormatter, then convert using Timestamp.valueOf(LocalDateTime).
Option 2 - more specific. If you are using different formats in your app you can make StudentDataDto to StudentMetaDataEntity converter
public class DtoToMetaConverter implements Converter<StudentDataDto, StudentMetaDataEntity> {
#Override
public StudentMetaDataEntity convert(MappingContext<StudentDataDto, StudentMetaDataEntity> mappingContext) {
StudentDataDto source = mappingContext.getSource();
StudentMetaDataEntity dest = new StudentMetaDataEntity();
//Convert string to timestamp like in other example
dest.setCreatedAt(...);
return dest;
}
}
Then register the converter and test it.
public class TimestampMain {
public static void main(String[] args) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.addConverter(new StringToTimestampConverter());
StudentDataDto source = new StudentDataDto();
source.setCreatedAt("2022-01-20T11:12:46");
StudentMetaDataEntity destination = modelMapper.map(source, StudentMetaDataEntity.class);
System.out.println(destination.getCreatedAt());
}
}
This example uses the more general option 1, but if you need option 2, just register the other converter in similar fashion.
I get unparseable date exception. After a lot of searches, I try with Locale.US and Locale.ENGLISH and with dateFormat: "yyyy-MM-dd HH:mm:ss z" but still the same error. So I try with an annotation from Jackson-dataFormat so I got this with this pattern and try with the pattern "yyyy-MM-dd HH:mm:ss z" too:
#Column(name="date", nullable=false)
#Temporal(TemporalType.DATE)
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ssZ")
private Date date;
My dateInString is: "2017-12-23T20:30:15.000Z".
I don't really know where can be the exception.
try {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
this.date = (Date)formatter.parse(dateInString);
}
catch(ParseException e) {
e.printStackTrace();
}
Maybe this information will help. In my Database, the column is named the date and is DATE type. (I have the choice in phpMyAdmin to choose DATE or DATETIME type).
At the beginning, I thought it was because I forgot the Locale.X parameter but still the exception.
Errors:
Caused by: com.owlike.genson.JsonBindingException: Could not parse date 2017-12-23T20:30:15.000Z
at com.owlike.genson.convert.DefaultConverters$DateConverter.deserialize(DefaultConverters.java:815)
at com.owlike.genson.convert.DefaultConverters$DateConverter.deserialize(DefaultConverters.java:781)
at com.owlike.genson.convert.NullConverter$NullConverterWrapper.deserialize(NullConverter.java:57)
at com.owlike.genson.reflect.PropertyMutator.deserialize(PropertyMutator.java:30)
... 46 more
Caused by: java.text.ParseException: Unparseable date: "2017-12-23T20:30:15.000Z"
at java.text.DateFormat.parse(DateFormat.java:366)
at com.owlike.genson.convert.DefaultConverters$DateConverter.read(DefaultConverters.java:821)
at com.owlike.genson.convert.DefaultConverters$DateConverter.deserialize(DefaultConverters.java:813)
... 49 more
So I get this errors too but it's because my parsing not work:
Caused by: com.owlike.genson.JsonBindingException: Could not deserialize to type class model.SessionActivity
at com.owlike.genson.Genson.deserialize(Genson.java:442)
at com.owlike.genson.ext.jaxrs.GensonJsonConverter.readFrom(GensonJsonConverter.java:125)
... 40 more
Caused by: com.owlike.genson.JsonBindingException: Could not deserialize to property 'date' of class class model.SessionActivity
at com.owlike.genson.reflect.PropertyMutator.couldNotDeserialize(PropertyMutator.java:49)
at com.owlike.genson.reflect.PropertyMutator.deserialize(PropertyMutator.java:32)
at com.owlike.genson.reflect.BeanDescriptor.deserialize(BeanDescriptor.java:110)
at com.owlike.genson.reflect.BeanDescriptor.deserialize(BeanDescriptor.java:93)
at com.owlike.genson.convert.BeanViewConverter.deserialize(BeanViewConverter.java:105)
at com.owlike.genson.convert.NullConverter$NullConverterWrapper.deserialize(NullConverter.java:57)
at com.owlike.genson.Genson.deserialize(Genson.java:440)
... 41 more
The subject already exist, I read them and try to resolve with these responses but none work actually.
Here is my POST json request:
Trying with ISO 8601: 2017-12-23T20:30:15.000Z
First, I handle POST request in WebService class:
#POST
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
public Response addSession(SessionActivity sessionActivity) {
SessionDAO.add(sessionActivity);
return Response.ok().build();
}
It call add() of DAO session:
public static void add(SessionActivity sessionActivity){
try {
session = SessionUtil.getSession();
Transaction tx = session.beginTransaction();
session.save(sessionActivity);
tx.commit();
session.close();
}
catch(Exception e) {
System.out.println( "ADD sessionActivity non effectué :" );
e.printStackTrace();
}
}
Then here is my model:
#Column(name="date", nullable=false)
#Temporal(TemporalType.DATE)
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ssZ")
private Date date;
public SessionActivity(Long id, User user, Date date) {
super();
this.id = id;
this.user = user;
this.date = date;
/*
try {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
this.date = (Date)formatter.parse(dateInString);
}
catch(ParseException e) {
e.printStackTrace();
}*/
}
The format for a json date is ISO 8601: 2017-12-23T20:30:15.000Z
A json coming with a value like this should parse into a java Date without having to do any conversion. The same the other way round.
By the way, Z at the end means it a Zulu or GMT 0 time. So after the value is parsed into a java Date you can use time zoned methods to get the value for a specific locale.
EDIT I
I think there is some issue with the parser you are using. I ran a test using the Genson parser you are using and the same test using Gson, with the same json text, and while Genson couldn't parse the date, Gson parsed it with no problem.
Here is the test, you have to comment the lines you don't want to execute.
Also note I had to annotate the DatedObject class constructor for Genson to find it (public DatedObject(#JaonParam("date") Date date, JsonParam("user") String user){)
import com.google.gson.Gson;
import com.owlike.genson.Genson;
import com.owlike.genson.annotation.JsonProperty;
public class JsonParseDate {
public String json = "{\"date\":\"2017-12-23T20:30:15.000Z\",\"user_fk\":\"1\"}";
public static void main(String [] args){
JsonParseDate app = new JsonParseDate();
app.go();
}
private void go(){
// Comment next two lines to run with Gson (and uncomment the Gson lines)
// Genson genson = new Genson();
// DatedObject datedObject = genson.deserialize(json, DatedObject.class);
// Comment next two lines to run with Genson (and uncomment Genson lines)
Gson gson = new Gson();
DatedObject datedObject = gson.fromJson(json, DatedObject.class);
System.out.println(datedObject.date);
}
public static class DatedObject{
public Date date;
public String user;
public DatedObject(Date date, String user){
this.date = date;
this.user = user;
}
}
}
Ref to Genson user guide
For Gson you can get the jar from maven or add the dependancy to your pom if you are using maven.
How to bind dateOfJoining value(String type) to member variable "dateOfJoining"(Date type) in "DateInput" Class after sending below JSON through postman. How to convert String to Date object in java with the same format dd/MM/yyyy. Converted date should be in Date object but not String.
Json is as given below
{
"dateOfJoining" : "03/04/2017"
}
Service URL hitting in postman -- localhost/Rest/hello
RestService class in java - HandleRestRequest.java
#RestController
public class HandleRestRequest
{
#RequestMapping("\hello");
public List setRequestParams(#RequestBody DateInput dateInput)
{
.......
}
}
Pojo Class DateInput.java
public class DateInput
{
private Date dateOfJoining;
.......
}
If I send date from json in below format, its throwing error as unsatisfied input
{
"dateOfJoining" : 04/04/2017
}
So I sending it as string format and by changing dateOfJoining as string in DateInput.java file and later I tried to convert it as date object as below
Modified DateInput.java file from Date to String
public class DateInput
{
private String dateOfJoining;
.......
}
Modified JSON
{
"dateOfJoining" : "04/04/2017"
}
Code to convert user input from String to Date
DateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
String convertedDate = sdf.format(dateInput.getDateOfJoining());
Its converting into required format but return type is String but expected is Date object to send DAO layer. So I tried sdf.parse, its returning Date object but not in required format
Date date = sdf.parse(sdf.format(dateInput.getDateOfJoining()));
output is like - Tue Apr 04 00:00:00 IST 2017
expected is 04/04/2017 (with return type Date object).
So please help me how to convert the string to date object with required format since DAO layer is expecting input as date object in format dd/MM/yyyy
Edit: Updating answer in accordance with updated question.
Use annotation #JsonFormat from Jackson Databind to specify the date pattern.
public class DateInput
{
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd-MM-yyyy")
private Date dateOfJoining;
.......
}
With JSON-B (included in Java EE 8) you can do this:
class DateInput {
#JsonbDateFormat("dd/MM/yyyy")
public Date dateOfJoining;
}
In my tests with Thorntail 2.4 I don't need any annotation for ISO format when using java.util.Date:
{
"dateOfJoining" : "2019-04-28T14:45:15"
}
change your code to the below code snippet
public List setRequestParams(#RequestParam("dateOfJoining")#DateTimeFormat(pattern="dd-MM-yyyy") DateInput dateInput)
{
...
}
I have the below Code :
DTO :
Class MyDTO {
import java.util.Date;
private Date dateOfBirth;
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
}
Controller
public void saveDOB(#RequestBody MyDTO myDTO, HttpServletRequest httprequest, HttpServletResponse httpResponse) {
System.out.println("Inside Controller");
System.out.println(myDTO.getDateOfBirth());
}
JSON Request :
{
"dateOfBirth":"2014-09-04",
}
If I send the request as yyyy-mm-dd automatic conversion to date object happens.
output in controller:-
dateOfBirth= Thu Sep 04 05:30:00 IST 2014
But when I send DateofBirth in dd-mm-yyyy format It does not convert String to Date automatically.So how i can i handle this case.
JSON Request :
{
"dateOfBirth":"04-09-2014",
}
Output: No Output in console does not even reaches controller.
I have tried with #DateTimeFormat but its not working.
I am using Spring 4.02 Please suggest is there any annotation we can use.
#DateTimeFormat is for form backing (command) objects. Your JSON is processed (by default) by Jackson's ObjectMapper in Spring's MappingJackson2HttpMessageConverter (assuming the latest version of Jackson). This ObjectMapper has a number of default date formats it can handle. It seems yyyy-mm-dd is one of them, but dd-mm-yyyy is not.
You'll need to register your own date format with a ObjectMapper and register that ObjectMapper with the MappingJackson2HttpMessageConverter. Here are various ways to do that :
Configuring ObjectMapper in Spring
Spring, Jackson and Customization (e.g. CustomDeserializer)
Alternatively, you can use a JsonDeserializer on either your whole class or one of its fields (the date). Examples in the link below
Right way to write JSON deserializer in Spring or extend it
How to deserialize JS date using Jackson?
List itemCreate a class to extend JsonDeserializer
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);
}
}
}
Use #JsonDeserialize(using = CustomJsonDateDeserializer.class) annotation on setter methods.
Thanks #Varun Achar answer, url