I have json string payload having date in epoch (long) format.But I have to convert that into yyyyMMddHHmmss format.I'm using custom serializers where I can apply that on particular field.But the serialization is not able to apply on that field.
Test.java
private static String json = "{
"dcCountryCode": "US",
"orderDate": 1517855400000
}";
#JsonSerialize(using = CustomLongSerializer.class)
private static Long date;
public static void main(String[] args) {
JSONObject obj = new JSONObject(json);
String country = obj.getString("dcCountryCode");
date = obj.getLong("orderDate");
System.out.println(country);
System.out.println(date);
}
CustomLongSerializer.java
package com.company;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import java.io.IOException;
import java.text.SimpleDateFormat;
// This is for Custom Date serializer
public class CustomLongSerializer extends StdSerializer<Long> {
protected CustomLongSerializer(Class<Long> t) {
super(t);
}
protected CustomLongSerializer() {
this(Long.class);
}
private static final long serialVersionUID = 1L;
#Override
public void serialize(Long value, JsonGenerator gen, SerializerProvider provider) throws IOException {
SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
gen.writeString(df.format(value));
}
}
Expected Out put in yyyyMMddHHmmss format.
But still returning epoch date format.
Can anyone help me with this.
It may be implemented in a simpler way by disabling SerializationFeature.WRITE_DATES_AS_TIMESTAMPS and setting DateFormatter in the mapper:
public class TestDate {
private String dcCountryCode;
private Date date;
// getters/setters
}
// test class
String json = "{\n" +
" \"dcCountryCode\": \"US\",\n" +
" \"date\": 1517855400000\n" +
" }";
ObjectMapper mapper = new ObjectMapper()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.setDateFormat(new SimpleDateFormat("yyyyMMddHHmmss"));
TestDate test = mapper.readValue(json, TestDate.class);
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(test));
Output:
{
"dcCountryCode" : "US",
"date" : "20180205203000"
}
Related
I have a JSON Data Object class as follows:
public class Plugins {
private String id;
private String name;
#JsonProperty("created_at")
private long createdAt;
}
Where createdAt is the long timestamp of the creation date. I use this class to back up a Jackson ObjectMapper object parsing JSON data from an external API call. I was wondering if it is possible to have Jackson convert created_at automatically to a readable date format and store in Java as a String or Date flavour?
if it is possible to have Jackson convert created_at automatically to a readable date format and store in Java as a String or Date flavour?
Date is obsolete and discouraged to be used.
Since Java 8 (which was released about 10 years ago) we have modern Time API which includes Instant, LocalDateTime and other classes from the java.time package.
You can change your POJO to make it store date-time information properly without the need to change the JSON payload. I.e. created_at can be received as a long value like 1665148545 and translated into ZonedDateTime (or other date-time representations like Istant, LocalDateTime).
public class Plugins {
private String id;
private String name;
private ZonedDateTime createdAt;
public Plugins(#JsonProperty("id") String id,
#JsonProperty("name") String name,
#JsonProperty("created_at") long createdAt) {
this.id = id;
this.name = name;
this.createdAt = Instant.ofEpochSecond(createdAt)
.atZone(ZoneId.of("UTC"));
}
// getters, toString(), etc.
}
Usage example:
String json = """
{
"id": "1",
"name": "someName",
"created_at": 1665148545
}""";
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(json, Plugins.class));
Output:
lugins{id='1', name='someName', createdAt=2022-10-07T13:15:45}
You just need to register JavaTimeModule module and use required type from Java-8 time package. Take a look on below example:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.Instant;
public class DateApp {
private final static JsonMapper JSON_MAPPER = JsonMapper.builder()
.enable(SerializationFeature.INDENT_OUTPUT)
.addModule(new JavaTimeModule())
.build();
public static void main(String[] args) throws Exception {
String json = "{\"id\": \"1\",\"name\":\"someName\",\"created_at\": 1665148545}";
Plugins plugins = JSON_MAPPER.readValue(json, Plugins.class);
System.out.println(plugins);
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
class Plugins {
private String id;
private String name;
#JsonProperty("created_at")
private Instant createdAt;
}
Above code prints:
Plugins(id=1, name=someName, createdAt=2022-10-07T13:15:45Z)
Using Custom Deserialiser in jackson
You can achieve the date conversion from long to String or Date by using the custom deserialiser. This custom deserialiser will convert the long value from the json into the defined date format(either Date or String).
Please Note: Here, I have converted the epoch value into the String datatype. In case if Date datatype is needed, you can change the implementation of the deserialize method of CustomDateSerializer class accordingly.
You need to use the below annotation to the fields on which custom deserialisation is required.
#JsonDeserialize(using = CustomDateSerializer.class)
Please find the code below:
Plugins.java
public class Plugins {
private String id;
private String name;
#JsonDeserialize(using = CustomDateSerializer.class)
#JsonProperty("created_at")
private String createdAt;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCreatedAt() {
return createdAt;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
#Override
public String toString() {
return "Plugins{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", createdAt='" + createdAt + '\'' +
'}';
}
}
CustomDateSerializer.java
public class CustomDateSerializer extends StdDeserializer<String> {
public static String pattern = "dd MMM yyyy hh:mm:ss";
public CustomDateSerializer() {
this(StdDeserializer.class);
}
protected CustomDateSerializer(Class<?> c) {
super(c);
}
#Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
SimpleDateFormat formatter = new SimpleDateFormat(pattern);
return formatter.format(new Date(jsonParser.getLongValue()));//change the implementation of deserialise method if date format is needed.
}
}
Test.java
public class Test {
public static void main(String[] args) throws JsonProcessingException {
//For sample input json, here i have used Text Blocks feature available from JDK 15 to have the string in readable format.
String json = """
{
"id":"1",
"name":"test",
"created_at":1665158083000
}
""";
ObjectMapper mapper = new ObjectMapper();
Plugins test = mapper.readValue(json,Plugins.class);
System.out.println(test);
}
}
Output:
Plugins{id='1', name='test', createdAt='07 Oct 2022 09:24:43'}
I am using to convert JSON String to POJO Class using Gson. In Pojo the attribute are of java.util.Date type. While gson maps Json String to Pojo objects it is removing timestamp.
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.S'Z'").create();
ClassBaseModel baseModel = gson.fromJson(request, ClassBaseModel.class);
Attribute Original Value in String Json is - "orderDate": "2021-12-01T07:16:31Z"
After It is converted into POJO - 2021-12-01
Expected is - 2021-12-01T07:16:31Z
I am not sure what wrong I am doing. Can somebody please point out.
While Deserializing you have to register deserializer type in gson using registerTypeAdapter
Here is how I have tried to do so, and also
Request class:
class OrderInfoRequest {
String orderDate;
public OrderInfoRequest(String orderDate) {
this.orderDate = orderDate;
}
}
Main Mapper class
class OrderInfo {
OffsetDateTime orderDate;
public OffsetDateTime getOrderDate() {
return orderDate;
}
public void setOrderDate(OffsetDateTime orderDate) {
this.orderDate = orderDate;
}
#Override
public String toString() {
return "OrderInfo{" +
"orderDate=" + orderDate +
'}';
}
}
Sample code
public class DateMain {
public static void main(String[] args) {
OrderInfoRequest orderInfoRequest = new OrderInfoRequest("2021-12-01T07:16:31Z");
Gson gson = new GsonBuilder()
.registerTypeAdapter(OffsetDateTime.class, (JsonDeserializer<OffsetDateTime>) (json, typeOfT, context) -> OffsetDateTime.parse(json.getAsString(), DateTimeFormatter.ISO_OFFSET_DATE_TIME))
.create();
String requestJson = gson.toJson(orderInfoRequest);
System.out.println("Request json");
System.out.println(requestJson);
OrderInfo orderInfo = gson.fromJson(requestJson, OrderInfo.class);
System.out.println("After parsing pojo");
System.out.println(orderInfo);
System.out.println("printing full date: " + orderInfo.orderDate.format(DateTimeFormatter.ofPattern("yyyy/MM/dd, hh:mm")));
}
}
Also as told by #Jens avoid using java.util.Date class from today onwards if you haven't.
I have a serialized json-date. I wonder if I can just deserialize that single string with the json-date like in c#. But how do you do that in java?
Here is the c# code:
DateTime dateTime = JsonConvert.DeserializeObject<DateTime>(string);
Hope someone can help and that I do not need to create a whole class to do this.
Edit:
Here is the string: "/Date(1630050818893+0300)/"
Since you are trying to deserialize the old .NET text date format, you will have to setup a GSON JsonDeserializer for the Date type.
You can implement one matching and extracting the timestamp value out of the String date as follows:
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DateDeserializer implements JsonDeserializer<Date> {
private static final Pattern DATE_PATTERN = Pattern.compile("/Date\\((-?\\d+).*\\)/");
#Override
public Date deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext)
throws JsonParseException {
final Matcher matcher = DATE_PATTERN.matcher(jsonElement.getAsString());
if (matcher.matches()) {
final String timestamp = matcher.group(1);
return new Date(Long.parseLong(timestamp));
} else {
throw new IllegalArgumentException(jsonElement.getAsString() + " does not match /Date(timestamp)/ pattern");
}
}
}
You can then register the custom DateDeserializer as your java.util.Date type deserializer (given that you are using this type in your POJO type):
Gson gson = new GsonBuilder()
.registerTypeAdapter(Date.class, new DateDeserializer())
.create();
ObjectWithDate objectWithDate = gson.fromJson(json, ObjectWithDate.class);
Where ObjectWithDate is a template object holding a java.util.Date field:
public class ObjectWithDate {
private final Date date;
public ObjectWithDate(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
#Override
public String toString() {
return "ObjectWithDate{" +
"date=" + date +
'}';
}
}
I have list of json objects,one of the fields is date. The problem is that the dates are written in diffrent ways in json.
most of them looks like:
"publishedDate": "2005-01-28"
"publishedDate": "2011-08-29"
"publishedDate": "2016-04-19"
But some of them is like:
"publishedDate": "1998-11"
"publishedDate": "2001-01"
My java object field to which i want to parse
private Date publishedDate;
I got this error:
Cannot deserialize value of type `java.util.Date` from String "2001-01": not a valid representation (error: Failed to parse Date value '2001-01': Cannot parse date "2001-01": while it seems to fit format 'yyyy-MM-dd', parsing fails (leniency? null))
You need to write custom deserialiser for a Date and in both cases properly convert to expected date. Below you can find simple example how to do that:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
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;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./resource/test.json").getAbsoluteFile();
ObjectMapper mapper = new ObjectMapper();
TypeReference<List<Item>> typeReference = new TypeReference<List<Item>>() {
};
List<Item> readValue = mapper.readValue(jsonFile, typeReference);
System.out.println(readValue);
}
}
class DifferentFormatsDateJsonDeserializer extends JsonDeserializer<Date> {
private DateTimeFormatter localDateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private DateTimeFormatter yearMonthFormatter = DateTimeFormatter.ofPattern("yyyy-MM");
#Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String value = p.getValueAsString();
try {
if (value.length() == 7) {
YearMonth yearMonth = YearMonth.parse(value, yearMonthFormatter);
return convertToDateViaInstant(yearMonth.atDay(1));
} else {
LocalDate localDate = LocalDate.parse(value, localDateFormatter);
return convertToDateViaInstant(localDate);
}
} catch (Exception e) {
System.out.println(e);
}
return null;
}
public Date convertToDateViaInstant(LocalDate dateToConvert) {
return Date.from(dateToConvert.atStartOfDay()
.atZone(ZoneId.systemDefault())
.toInstant());
}
}
class Item {
#JsonDeserialize(using = DifferentFormatsDateJsonDeserializer.class)
private Date publishedDate;
public Date getPublishedDate() {
return publishedDate;
}
public void setPublishedDate(Date publishedDate) {
this.publishedDate = publishedDate;
}
#Override
public String toString() {
return "Item{" +
"publishedDate=" + publishedDate +
'}';
}
}
Above program for JSON payload:
[
{
"publishedDate": "2005-01-28"
},
{
"publishedDate": "1998-11"
}
]
Prints:
[Item{publishedDate=Fri Jan 28 00:00:00 CET 2005}, Item{publishedDate=Sun Nov 01 00:00:00 CET 1998}]
I have the following code:
import javax.xml.datatype.XMLGregorianCalendar;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
public class Test {
public static void main(String[] args) {
print("");//Prints null
print("2013-06-14T01:23:47.547+0000"); //Prints the date
print("&&&&AD");//Throws error
}
private static void print(String dateString) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
try {
String x = "{\"dateTime\": \""+dateString+"\"}";
Foo type = mapper.readValue(x, Foo.class);
System.out.println("Datetime is " + type.getDateTime());
} catch (Exception e) {
e.printStackTrace();
}
}
private static class Foo {
private XMLGregorianCalendar dateTime;
public XMLGregorianCalendar getDateTime() {
return dateTime;
}
public void setDateTime(XMLGregorianCalendar dateTime) {
this.dateTime = dateTime;
}
}
}
When the String value is blank "", then Jackson treats the value as null, but when I put some invalid value such as "&&&&AD", it tries to convert it to XML date Time and throws error.
The error I get is:
com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of javax.xml.datatype.XMLGregorianCalendar from String value '&&&&AD': not a valid representation (error: Failed to parse Date value '&&&&AD': Can not parse date "&&&&AD": not compatible with any of standard forms ("yyyy-MM-dd'T'HH:mm:ss.SSSZ", "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", "EEE, dd MMM yyyy HH:mm:ss zzz", "yyyy-MM-dd"))
I would like to see the same behavior for blank value. How do I do it?
Is there a way to configure Jackson to fail for blank value?
You have to implement new deserializer for XMLGregorianCalendar type. It would look like this:
class XMLGregorianCalendarDeserializer extends GregorianCalendarDeserializer {
private static final long serialVersionUID = 1L;
#Override
public XMLGregorianCalendar deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException {
try {
return super.deserialize(jp, ctxt);
} catch (JsonProcessingException e) {
return null;
}
}
}
After that you have to define deserializer in POJO class:
class Foo {
#JsonDeserialize(using = XMLGregorianCalendarDeserializer.class)
private XMLGregorianCalendar dateTime;
...
}