Unable to store Date in a specific format in database - java

reading json data from database, and converting it into a object in java.
Data in Database :- {"id":123,"expiryDate":"2024-02-10"}
NOTE :- I am trying to use this data in Google Web Toolkit (GWT)
converting above data using object mapper.
Object consuming above json TestJson.java
Class TestJson {
private Long id,
private Date expiryDate;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id= id;
}
public Date getExpiryDate() {
return expiryDate;
}
public void setExpiryDate(Date expiryDate) {
this.expiryDate = expiryDate;
}
public String toJSONString() {
return "{" +
"\"id\" : \"" + id + "\"," +
"\"expiryDate\" : \"" + expiryDate + "\"}";
}
}
When the Json data is converted then the value of "Expiry Date" is "Sat Feb 10 05:30:00 IST 2024".
But I want it in the same format as it is in JSON.

Since I was trying to save/parse the object in Google Web Toolkit (GWT) so SimpleDateFormat was creating issue, as GWT doesn't supports SimpleDateFormat.
But below worked for me
import com.google.gwt.i18n.shared.DateTimeFormat;
import com.google.gwt.i18n.shared.DefaultDateTimeFormatInfo;
String pattern = "yyyy-MM-dd"; /*your pattern here*/
DefaultDateTimeFormatInfo info = new DefaultDateTimeFormatInfo();
DateTimeFormat df = new DateTimeFormat(pattern, info) {};
df.format(expiryDate)
Output :-
{"id":123,"expiryDate" : "2024-02-10"}

Related

Converting JSON long to a date in Java via Jackson

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'}

Gson Removing Timestamp From Date while Conversion

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.

How to deserialize a String to a Date with Morphia

I have a Mongo collection with objects of this format:
{
id: 1,
date: "2020-08-06T12:00:00Z",
...
}
I have Java code that needs to read from this collection but never writes to it. The process that writes to this collection is not owned by me so I can't necessarily change the format of that date string. I initially tried to model my Java Morphia object like this:
#Entity public class MyDocument {
#Id
private Integer id;
private Date date;
...
}
This did not work because Morphia didn't know how to deserialize that date format into a Date object. The solution that I came up with was treating the date as a String on the POJO and then having a getDate() method that did the actual deserialization. I am wondering, is there a better way for me to do this? I know if you're using Jackson you can annotate certain fields with #JsonDeserialize and pass a deserializer so I was wondering if there was something similar for Morphia.
My solution (which feels suboptimal to me):
#Entity public class MyDocument {
#Id
private Integer id;
private String date;
...
private Date getDate() {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
try {
return dateFormat.parse(date);
} catch (Exception ex) {
return null;
}
}
}
You can go ahead and create a simple converter extending the TypeConverter like so:
public class DateConverter extends TypeConverter {
private static final String FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private final SimpleDateFormat simpleDateFormat;
public DateConverter() {
super(Date.class);
this.simpleDateFormat = new SimpleDateFormat(FORMAT);
}
#Override
public Object decode(Class<?> targetClass, Object fromDBObject, MappedField optionalExtraInfo) {
try {
return simpleDateFormat.parse(((String) fromDBObject));
} catch (ParseException e) {
return null;
}
}
}
The go ahead and register your formatter for your document entity like so:
#Entity("Documents")
#Converters(DateConverter.class)
public class Document {
#Id
private Integer id;
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Date date;
public Date getDate() { return date; }
public void setDate(Date date) { this.date = date; }
#Override
public String toString() {
return "Document{" +
"id=" + id +
", date=" + date +
'}';
}
}
This will effectively tell Morphia to decode the database incoming values via parsing the string with the desired pattern, resulting directly into a concrete Date object without any additional conversion logic.

Room Abstract Pojo

I'm creating for fun an android application that tracks the spendings. I'm using Room to persist the user's data and I have POJOs that show the daily/weekly/monthly summaries.
These classes are quite similar, thus I would like to have one abstract POJO that contains the fields and extensions of it that reformat to the correct format. Something like:
public abstract class PeriodInformation {
PeriodInformation(#NonNull Calendar mCalendar, Integer mPeriodSpendingCount, Float mPeriodSpendingSum) {
this.mCalendar = mCalendar;
this.mPeriodSpendingCount = mPeriodSpendingCount;
this.mPeriodSpendingSum = mPeriodSpendingSum;
}
#ColumnInfo(name = "DateTime")
private final Calendar mCalendar;
#ColumnInfo(name = "SpendingCount")
private Integer mPeriodSpendingCount;
#ColumnInfo(name = "SpendingSum")
private Float mPeriodSpendingSum;
// Some other code, e.g., getters, equal override,...
}
Here the extension:
public class WeekInformation extends PeriodInformation{
public WeekInformation(#NonNull Calendar mCalendar, Integer mPeriodSpendingCount, Float mMonthSpendingSum) {
super(mCalendar, mPeriodSpendingCount, mMonthSpendingSum);
}
#Override
public String getPeriodRepresentation() {
//return representation;
}
}
However, I get following error message for the WeekInformation Class:
error: Entities and Pojos must have a usable public constructor. You can have an empty constructor or a constructor whose parameters match the fields (by name and type).
So it seems that this is not possible in Room, thus I would be happy to get some suggestion how to not have to copy the same code too often.
thank you.
EDIT:
I use following DAO code to aggregate to the POJO, the column calendarDate has following format "yyyy-MM-dd'T'HH:mm:ss.SSSXXX":
#Query("SELECT date(datetime(calendarDate)) AS 'DateTime', count(uID) AS 'SpendingCount', sum(value) AS 'SpendingSum' from spending GROUP BY date(datetime(calendarDate))")
LiveData<List<DayInformation>> loadDayInformation();
I was able to make this work for me, using the Embedded annotation, allowing direct access to the fields of the embedded data type.
public class DayInformation {
#Embedded
public PeriodInformation periodInformation;
#Override
public String toString() {
return "DayInformation{" +
"periodInformation=" + periodInformation +
'}';
}
}
and
public class PeriodInformation {
PeriodInformation(Calendar timestamp,
int periodSpendingCount,
float periodSpendingSum) {
this.timestamp = timestamp;
this.periodSpendingCount = periodSpendingCount;
this.periodSpendingSum = periodSpendingSum;
}
#ColumnInfo(name = "DateTime")
public final Calendar timestamp;
#ColumnInfo(name = "SpendingCount")
public Integer periodSpendingCount;
#ColumnInfo(name = "SpendingSum")
public Float periodSpendingSum;
#Override
public String toString() {
final DateFormat dateInstance = SimpleDateFormat.getDateInstance();
String date = timestamp == null ? "null" : dateInstance.format(timestamp.getTime());
return "PeriodInformation{" +
"timestamp='" + date + '\'' +
", periodSpendingCount=" + periodSpendingCount +
", periodSpendingSum=" + periodSpendingSum +
'}';
}
}
plus
#Entity
public class Spending {
#PrimaryKey(autoGenerate = true)
public int uid;
#ColumnInfo(name = "calendarDate")
public Calendar timestamp;
#ColumnInfo(name = "value")
public float value;
public Spending(#NonNull Calendar timestamp, float value) {
this.timestamp = timestamp;
this.value = value;
}
#Override
public String toString() {
final DateFormat dateInstance = SimpleDateFormat.getDateInstance();
String date = timestamp == null ? "null" : dateInstance.format(timestamp.getTime());
return "Spending{" +
"uid=" + uid +
", timestamp='" + date + '\'' +
", value=" + value +
'}';
}
}
and a DAO
#Dao
public interface SpendingDao {
#Insert
void insertAll(Spending... spendings);
#Query("SELECT * FROM spending")
LiveData<List<Spending>> findAll();
#Query("SELECT calendarDate AS 'DateTime', count(uID) AS 'SpendingCount', sum(value) AS 'SpendingSum' from spending GROUP BY date(datetime(calendarDate))")
LiveData<List<DayInformation>> loadDayInformation();
}
gives the following output
aggregated data is
DayInformation{periodInformation=PeriodInformation{timestamp='Jun 26, 2018', periodSpendingCount=8, periodSpendingSum=184.0}}
spending data is Spending{uid=1, timestamp='Jun 26, 2018', value=23.0}
spending data is Spending{uid=2, timestamp='Jun 26, 2018', value=23.0}
spending data is Spending{uid=3, timestamp='Jun 26, 2018', value=23.0}
spending data is Spending{uid=4, timestamp='Jun 26, 2018', value=23.0}
spending data is Spending{uid=5, timestamp='Jun 26, 2018', value=23.0}
spending data is Spending{uid=6, timestamp='Jun 26, 2018', value=23.0}
spending data is Spending{uid=7, timestamp='Jun 26, 2018', value=23.0}
spending data is Spending{uid=8, timestamp='Jun 26, 2018', value=23.0}

Play! framework bootstrap(Fixtures.loadModels("initial-data.yaml)), injecting "random" String value in model's setter

I have a model class with a persisted DateTime field that is only interacted via getters/setters for Transient properties, String date; and String time;. The do some very specific formatting to create the DateTime object that will be persisted or retrieved when need be.
The problem is that when my model's loaded from the yaml file, the setter for the time field receives a String value that doesn't correspond at all to anything in my project/code.
Here's the class with only relevant members:
package models;
import javax.persistence.*;
import org.hibernate.annotations.*;
import org.joda.time.*;
import org.joda.time.format.*;
import play.db.jpa.*;
#javax.persistence.Entity
public class Booking extends Model {
#Column
#Type(type = "org.joda.time.contrib.hibernate.PersistentDateTime")
public DateTime datetime;
public Integer duration;
#Transient
public String date;
#Transient
public String time;
//default constructor called by play's model loader that sets default values that are required for the getters and setters to work.
public Booking() {
DateTimeFormatter fmt = DateTimeFormat.forPattern("'ISO8601':yyyy-MM-dd'T'HH:mm:ssZ");
this.datetime = fmt.parseDateTime("ISO8601:1970-01-01T00:00:00+0200");
//this.datetime = fmt.parseDateTime(this.date+"T"+this.time);
}
public void setDate(String dateStr) {
this.date = dateStr;
if (dateStr.contains("ISO")) {
DateTimeFormatter dt = DateTimeFormat.forPattern("'ISO8601':yyyy-MM-dd'T'HH:mm:ssZ");
DateTime tmp = dt.parseDateTime(dateStr);
this.datetime = toDateTime(tmp.toString("yyyy-MM-dd"), getTime());
} else {
this.datetime = toDateTime(dateStr, getTime());
}
}
public void setTime(String timeStr) {
this.time = timeStr; //timeStr = "780" for some reason?!
if (timeStr.contains("ISO")) {
DateTimeFormatter dt = DateTimeFormat.forPattern("'ISO8601':yyyy-MM-dd'T'HH:mm:ssZ");
DateTime tmp = dt.parseDateTime(timeStr);
this.datetime = toDateTime(getDate(), tmp.toString("HH:mm"));
}
this.datetime = toDateTime(getDate(), timeStr);
}
public String getDate() {
DateTimeFormatter format = DateTimeFormat.forPattern("yyyy-MM-dd");
return this.datetime.toString(format);
}
public String getTime() {
DateTimeFormatter format = DateTimeFormat.forPattern("HH:mm");
return this.datetime.toString(format);
}
private DateTime toDateTime(String dateStr, String timeStr) {
DateTimeFormatter fmt = ISODateTimeFormat.dateHourMinute();
DateTime dt = fmt.parseDateTime(dateStr + "T" + timeStr);
return dt;
}
When I run through the debugger, the timeStr parameter that setTime receives when it's first called is "780". There is no such value in my yaml file as the model is injected like this:
Booking(bobBooking):
date: 2011-09-16
time: 13:00
duration: 30
headcount: 10
room: b
user: bob
description: Bob's Booking.
The additional fields are omitted.
Try using quotes for time value in yaml file. There could be some issue in parsing colon fields using SnakeYAML parser (which is the default in Play)
YAML 1.1 defines 13:00 as a sexagesimal value (which is not what you expect)
http://yaml.org/type/int.html
Use single or double quotes to specify a string value. ('13:00', "13:00")

Categories