Trying to deserialize date with specific pattern from json file.
Object which I want to receive from json file:
#Data
public class MyClass {
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'UTC'")
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime date;
}
Json file:
{
"date" : "2017-01-01T00:00:59.000UTC"
}
Code example how I want to receive it:
ObjectMapper mapper = new ObjectMapper();
MyClass clazz = mapper.readValue(new File("MyFile.json"), MyClass.class);
Actual result:
com.fasterxml.jackson.databind.exc.InvalidFormatException:
Cannot deserialize value of type `java.time.LocalDateTime` from String "2017-01-01T00:00:59.000UTC":
Failed to deserialize java.time.LocalDateTime: (java.time.format.DateTimeParseException)
Text '2017-01-01T00:00:59.000UTC' could not be parsed, unparsed text found at index 23
at [Source: (File); line: 2, column: 11] (through reference chain: com.example.MyClass["date"])
How to deserialize current date pattern?
The date format that you are using is incorrect.
Instead of: yyyy-MM-dd'T'HH:mm:ss.SSS'UTC'
it should be: yyyy-MM-dd'T'HH:mm:ss.SSSz
Secondly, you need to use #JsonFormat to specify the date format.
#JsonFormat which is defined in jackson-databind package gives you more control on how to format Date and Calendar values according to SimpleDateFormat.
By using this, the POJO MyClass would look something like this:
#Data
public class MyClass {
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSz", timezone = "UTC")
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime date;
}
Now, if you try to deserialize using:
ObjectMapper mapper = new ObjectMapper();
MyClass clazz = mapper.readValue(new File("MyFile.json"), MyClass.class);
System.out.println(myClass);
Then the process would go through, producing a result something like this:
MyClass{date=2017-01-01T00:00:59.000}
Your date is in incorrect format (with UTC as text simply appended), but you can solve it by custom formatter.
public class Test {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
MyClass localDateTime = objectMapper.readValue("{\"date\":\"2017-01-01T00:00:59.000UTC\"}", MyClass.class);
System.out.println(localDateTime.date);
}
#Data
public static class MyClass {
#JsonDeserialize(using = CustomDeserializer.class)
private LocalDateTime date;
}
public static class CustomDeserializer extends LocalDateTimeDeserializer {
public CustomDeserializer() {
super(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}
protected CustomDeserializer(LocalDateTimeDeserializer base, Boolean leniency) {
super(base, leniency);
}
#Override
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String substring = jsonParser.getText().substring(0, jsonParser.getText().indexOf("U"));
return LocalDateTime.parse(substring, _formatter);
}
}
}
Try removing #JsonDeserialize. (In any case, you are trying to deserialize your date into LocalDateTime but it has time zone info, you would need to try ZonedDateTime or OffsetDateTime). And change the line
#DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'UTC'")
to
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
Here is the link to the question that has a full answer for you: Spring Data JPA - ZonedDateTime format for json serialization
Related
I'm trying to serialize a Timestamp Object to a JSON. But the object in the JSON is displayed as seconds.
This is a snippet of my POJO:
#JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
public class TimeAndDateDetail{
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh.mm.ss")
private Timestamp timeAndDate;
public Timestame getTimeAndDate() {return timeAndDate; }
public void setTimeAndDate(Timestamp timeAndDate){
this.timeAndDate = timeAndDate;
}
}
This is my output:
{
"timeAndDate": 1583038800000
}
Why is this happening? And how can I get it to keep its original format?
You can annotate the field with #JsonFormat to specify the format, that the timestamp will be serialized. Here is an example:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm")
Looks like you are using jackson, and this is the default behaviour of it.
best way is to disable the related object mapper feature:
objectMapper
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
I have a simple rest service to store time range, however, Spring cannot parse datetime format with timezone correctly.
the Entity is
#Data
#Entity
public class TimeRange {
#Setter(AccessLevel.NONE)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = true)
private LocalDateTime startTime;
#Column(nullable = true)
private LocalDateTime endTime;
}
The controller is:
#PostMapping(path = "/time", consumes = "application/json", produces = "application/json")
public Boolean setTime(#RequestBody TimeRange timeRange) {
timeRangeRepository.save(timeRange);
return true;
}
and the actuall request is
url = f'http://localhost/api/time'
data = {
"startTime": "2019-12-03T19:58:29.047820+08:00",
"endTime": "2019-12-04T19:58:29.047820+08:00"}
resp = requests.post(url, json=data, timeout=10)
pprint(resp.json())
spring reported an error said:
esolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error:
Cannot deserialize value of type `java.time.LocalDateTime` from String "2019-12-
03T19:58:29.047820+08:00": Failed to deserialize java.time.LocalDateTime:
(java.time.format.DateTimeParseException) Text '2019-12-03T19:58:29.047820+08:00' could not be
parsed, unparsed text found at index 26; nested exception is
com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type
java.time.LocalDateTime from String "2019-12-03T19:58:29.047820+08:00": Failed to deserialize
java.time.LocalDateTime: (java.time.format.DateTimeParseException) Text '2019-12-
03T19:58:29.047820+08:00' could not be parsed, unparsed text found at index 26
at
You have a date with offset, if all your date comes in the same format you can create a custom deserializer like this
public class CustomLocalDateTimeDeserializer extends StdDeserializer<LocalDateTime> {
private static final long serialVersionUID = 1L;
public CustomLocalDateTimeDeserializer () {
this(null);
}
protected CustomLocalDateTimeDeserializer (Class<?> vc) {
super(vc);
}
#Override
public LocalDateTime deserialize(JsonParser arg0, DeserializationContext arg1)
throws IOException, JsonProcessingException {
return LocalDateTime.parse(arg0.getValueAsString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
}
}
and the annotate your fields with #JsonDeserialize
#JsonDeserialize(using = CustomLocalDateTimeDeserializer.class)
private LocalDateTime startTime;
#JsonDeserialize(using = CustomLocalDateTimeDeserializer.class)
private LocalDateTime endTime;
And if you want to serialize your dates with the same format, you have to create a custom serializer
Annotate your LocalDateTime fields with:
#JsonSerialize(using = LocalDateTimeSerializer.class)
I have the following POJO which I use to send out as messages to rabbitmq:
public class MyMessage {
private String id;
private String name;
private Date createdDt;
#JsonCreator
public MyMessage(
#JsonProperty("id") String id,
#JsonProperty("name") String name,
#JsonProperty("createdDt") Date createdDt
) {
this.id = id;
this.name = name;
this.createdDt = createdDt;
}
}
The problem with this is that when I send it using rabbitTemplate.convertAndSend(), the createdDt will be in unix timestamp in the JSON message. I need the createdDt in the JSON after serialised to be in the format of dd-MM-yyyy HH:mm:ss.
I don't want to change the createdDt property in MyMessage class to be a string in that formatted date because I may want to use the POJO else where in the code and having the date as a string is not convenient later. It also doesn't sound "right" to have the date in string just for the purpose of having it in a particular format.
When I'm receiving the message back, I also need to deserialise that string date in the format of dd-MM-yyyy HH:mm:ss back into a Date object.
How can I keep the createdDt as a Date object while sending the date in a different format when serialised and then have the string deserialised back as a date object?
If you must use java.util.Date then just add the following annotation onto the createdDt field
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss")
private Date createdDt;
I recommend not using java.util.Date but preferring the Java 8+ Time API. In that case you can import Jackson's built-in support via module com.fasterxml.jackson.datatype:jackson-datatype-jsr310 and ...
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
... will, by default, render LocalDateTime as an ISO 8601 string.
Ref: https://fasterxml.github.io/jackson-datatype-jsr310/javadoc/2.7/com/fasterxml/jackson/datatype/jsr310/JavaTimeModule.html
With that specific requirements regarding serialzation/deserialization of the field I would suggest using custom serializer/deserializer.
public class CustomDateSerializer extends StdSerializer<Date> {
#Override
public void serialize(Date value, JsonGenerator generator, SerializerProvider provider)
throws IOException, JsonProcessingException {
// your implementation
}
}
public class CustomDateDeserializer extends StdDeserializer<Item> {
#Override
public Date deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// your implementation
}
}
Then you can simply mark createdDt like this:
public class MyMessage {
private String id;
private String name;
private Date createdDt;
#JsonCreator
public MyMessage(
#JsonProperty("id") String id,
#JsonProperty("name") String name,
#JsonProperty("createdDt") #JsonDeserialize(using = CustomDateDeserializer.class) #JsonSerialize(using = CustomDateSerializer.class) Date createdDt
) {
this.id = id;
this.name = name;
this.createdDt = createdDt;
}
}
This way you instruct Jackson to use your specific serializer/deserializer on a specific field.
If you would like to make the configuration to be applied on ObjectMapper level you can achieve it with module registration like that:
SimpleModule myModule = new SimpleModule();
myModule.addSerializer(Date.class, new CustomDateSerializer());
myModule.addDeserializer(Date.class, new CustomDateDeserializer());
objectMapper.registerModule(myModule);
I have a spring app in which I am using the #JsonFormat annotation to deserialize a date format. But when I sent an array of elements my entire payload fails even if one of the entries have an invalid date.
Is there a way I can surpass this by gracefully handling this exception, by either replacing the failed date with a default value or ignoring that array entry.
jackson.version: 2.7.5,
spring.version: 5.0.0.RELEASE
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
private Date date;
You could write a custom deserializer for your class where you set a default value in case something goes wrong. Something like:
public class MyJsonDateDeserializer extends JsonDeserializer<Date>
{
#Override
public Date deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
String date = jsonParser.getText();
try {
return format.parse(date);
} catch (ParseException e) {
return new Date();
}
}
}
Then on your class you could do something like:
class MyClass {
//...Fields
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
#JsonDeserialize(using = MyJsonDateDeserializer.class)
private Date date;
//...Fields
}
You could also add #JsonIgnoreProperties(ignoreUnknown = true) over your class if you know that its value is not necessary always.
I have the following JSON string in REST response:
"09:41:50 CET"
For the corresponding POJO mapper class has a Date type for this field. So I've tried Jackson and GSON to map JSON to Java Object, but both failed with the following messages:
GSON: java.text.ParseException: Failed to parse date ["09:41:50 CET"]: Invalid number: 09:4
Jackson: InvalidFormatException: Cannot deserialize value of type `java.util.Date` from
String "09:41:50 CET": not a valid representation
Sadly I cannot modify in the POJO class the type to string or anything else, because I get those POJO classes from mvn dependency.
Try with this:
public static void main(String[] args) throws ParseException {
String jsonStr = "{ \"date\" : \"09:41:50 CET\" }";
Gson gson = new GsonBuilder().setDateFormat("HH:mm:ss").create();
JsonElement element = gson.fromJson (jsonStr, JsonElement.class);
OnlyDate date =gson.fromJson(element, new TypeToken<OnlyDate>(){}.getType());
System.out.println(date.getDate());
}
My example DTO is:
public class OnlyDate implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#SerializedName("date")
private Date date ;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
You have to specify the dateFormat of your gson Element
Not sure what kind of rest you have however if you are using spring rest you can do it by implementing custom Converter check the example at https://www.baeldung.com/spring-mvc-custom-data-binder.
Since Jackson v2.0, you can use #JsonFormat annotation directly on Object members;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss", timezone="CET")
private Date date;