How to serialize Java ZonedDateTime to XML file - java

Error while serializing ZonedDateTime (it doesn't appear in the output xml at all):
java.lang.InstantiationException: java.time.ZonedDateTime
Continuing ...
java.lang.RuntimeException: failed to evaluate: =Class.new();
Continuing ...
I have an instance of the class, where one of the fields is of a type ZonedDateTime.
When i'm trying to serialize the object with XMLEncoder:
import java.beans.XMLEncoder;
I get this errors. In the output file all the other fields appear except the field with ZonedDateTime
this field with ZonedDateTime looks like this e.g.:
ZonedDateTime date = ZonedDateTime.parse("2010-01-10T00:00:00Z[CET]");
Is there a way to convert it to such date format that it will work?
e.g.
ZonedDateTime.parse("2010-01-10T00:00:00").toLocalDateTime().atZone(ZoneId.of("CET")
The above writing (with .toLocalDateTime()) may not make any sense, but it's just the example.
I'm actually serializing the whole list of these objects, so the error appears many times (and always no output in xml file)

XMLEncoder and XMLDecoder are meant to work with regular Java bean classes. Typically, these are classes which have a public zero-argument constructor and public property accessor methods. There is some support for other classes, such as those with constructors that take property values, but most java.time classes are different and there is no built-in support for them.
Fortunately, you can provide your own support, by specifying a PersistenceDelegate for each non-Java-bean class you plan to serialize.
So, the first step is providing a PersistenceDelegate for ZonedDateTime:
PersistenceDelegate zonedDateTimeDelegate = new PersistenceDelegate() {
#Override
protected Expression instantiate(Object target,
Encoder encoder) {
ZonedDateTime other = (ZonedDateTime) target;
return new Expression(other, ZonedDateTime.class, "of",
new Object[] {
other.getYear(),
other.getMonthValue(),
other.getDayOfMonth(),
other.getHour(),
other.getMinute(),
other.getSecond(),
other.getNano(),
other.getZone()
});
}
};
encoder.setPersistenceDelegate(
ZonedDateTime.class, zonedDateTimeDelegate);
But it turns out this is not enough, because the parts of the ZonedDateTime also get serialized, and one of them is a ZoneId. So we also need a PersistenceDelegate for ZoneId.
That PersistenceDelegate is easy to write:
PersistenceDelegate zoneIdDelegate = new PersistenceDelegate() {
#Override
protected Expression instantiate(Object target,
Encoder encoder) {
ZoneId other = (ZoneId) target;
return new Expression(other, ZoneId.class, "of",
new Object[] { other.getId() });
}
};
But registering it is not as easy. encoder.setPersistenceDelegate(ZoneId.class, zoneIdDelegate); won’t work, because ZoneId is an abstract class, which means there are no ZoneId objects, only instances of subclasses. XMLEncoder does not consult inheritance when checking for PersistenceDelegates. There must be a PersistenceDelegate for each class of every object to be serialized.
If you’re only serializing one ZonedDateTime, the solution is easy:
encoder.setPersistenceDelegate(
date.getZone().getClass(), zoneIdDelegate);
If you have a collection of them, you can check all of their ZoneId classes:
Set<Class<? extends ZoneId>> zoneClasses = new HashSet<>();
for (ZonedDateTime date : dates) {
Class<? extends ZoneId> zoneClass = date.getZone().getClass();
if (zoneClasses.add(zoneClass)) {
encoder.setPersistenceDelegate(zoneClass, zoneIdDelegate);
}
}
If you have aggregate objects containing ZonedDateTimes, you can simply iterate through them in a similar manner and access those ZonedDateTime values.

Related

Java Custom Data Type

I wanted to create a class with a custom data type that returns the class object. Consider a class Custom:
public class Custom {
// Some fields.
public Custom(String custom) {
// Some Text.
}
// Some Methods.
public void customMethod() {
// Some Code.
}
}
Now, consider a second class TestCustom:
public class TestCustom {
public static void main(String[] args) {
Custom custom = new Custom("Custom");
System.out.println(custom); // This should print "Custom"
custom.customMethod(); // This should perform the action
}
}
So, the question how to get the value custom on instantiating an object instead of memory location. Like what I get is:
Custom#279f2327
The java.util.Date class returns the current date. This can be seen as the constructor for the class is
public Date() {
this(System.currentTimeMillis());
}
For example, the following code would print out the current date:
DateFormat format = new SimpleDateFormat("dd/MM/yyyy");
Date date = new Date();
System.out.println(format.format(date));
The Answer by ML72 is correct and should be accepted. The java.util.Date constructor captures the current moment in UTC.
java.time
The java.util.Date class is terrible, for many reasons. That class is now legacy, supplanted years ago but the java.time classes as of the adoption of JSR 310.
The java.time classes avoid constructors, instead using factory methods.
The replacement for java.util.Date is java.time.Instant. To capture the current moment in UTC, call the class method .now().
Instant instant = Instant.now() ;
If you want the current moment as seen through the wall-clock time used by the people of a particular region (a time zone), use ZoneId to get a ZonedDateTime object. Notice again the factory method rather than a constructor.
ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
Adjust to UTC by extracting an Instant.
Instant instant = zdt.toInstant() ;
Override the toString() method, as it is automatically invoked when you try to display an object:
Add a field. For example;
private String value;
In the constructor, add the following code:
value = custom;
this will assign a value passed to the constructor as a parameter, to the value field.
And finally override the toString() method as follows:
#Override
public String toString() {
return value;
}
Now, when you display the value of the custom object, the overridden toString() method will be invoked and the argument will be displayed instead of the memory address. Whereas methods of the object will work as they are programmed to work. There is nothing to be changed with them.

Sort Array object by date of its field

I have an Object MyTimes and in that object there are fields name ,start_date and configuration.
I have an array of this object, MyTimes [] mytimes
I am trying to sort the array by the start time but am struggling how to go about it.
The start_time field is a string, so this needs converting to a datetime.
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
for(int i=0; i<mytimes.length; i++) {
Date date = formatter.parse(mytimes[i].getStartTime());
}
I'd then put the date into an array list perhaps and then sort by datetime? But then I wouldnt know which start_time corresponds with which mytimes object...
What is the most efficient way of doing this?
Under the right circumstances this is a one-liner:
Arrays.sort(myTimes, Comparator.comparing(MyTimes::getStartDate));
Let’s see it in action:
MyTimes[] myTimes = {
new MyTimes("Polly", "2019-03-06T17:00:00Z"),
new MyTimes("Margaret", "2019-03-08T09:00:00Z"),
new MyTimes("Jane", "2019-03-01T06:00:00Z")
};
Arrays.sort(myTimes, Comparator.comparing(MyTimes::getStartDate));
Arrays.stream(myTimes).forEach(System.out::println);
Output:
Jane 2019-03-01T06:00:00Z
Polly 2019-03-06T17:00:00Z
Margaret 2019-03-08T09:00:00Z
I am assuming that getStartDate returns an Instant or another type the natural order of which agrees with the chronological order you want. For example:
public class MyTimes {
private String name;
private Instant startDate;
// Constructor, getters, toString, etc.
}
If you are receiving your start dates as strings somehow, you may write a convenient constructor that accepts a string for start date. I am already using such a constructor in the above snippet. One possibility is having two constructors:
public MyTimes(String name, Instant startDate) {
this.name = name;
this.startDate = startDate;
}
public MyTimes(String name, String startDate) {
this(name, Instant.parse(startDate));
}
The Instant class is part of java.time, the modern Java date and time API.
I am exploiting the fact that your strings are in the ISO 8601 format for an instant, the format that Instant.parse accepts and parses.
Avoid SimpleDateFormat and Date
I recommend you don’t use SimpleDateFormat and Date. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. There is also an error in your format pattern string for parsing: Z (pronounced “Zulu”) means UTC, and of you don’t parse it as such, you will get incorrect times (on most JVMs). Instant.parse efficiently avoids any problems here.
Don’t store date-tine as a string
It looks like you are are storing start time in a String field in your object? That would be poor modelling. Use a proper date-time type. Strings are for interfaces. Date-time classes like Instant offer much more functionality, for example define sort order.
You have two main approaches:
Make your class implement Comparable
Use a custom Comparator
Then, you can choose the field to compare from, and transform it.
IE (implementing comparable):
class Example implements Comparable<Example> {
private String stringDate;
public int compareTo(Example e) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Date date1 = formatter.parse(this.stringDate);
Date date2 = formatter.parse(e.stringDate);
return date1.getTime() - date2.getTime();
}
}
And then using Arrays.sort would use your custom comparison.
Let your class implement Comparable and implement compareTo using modern formatting and date classes. Note that LocalDateTime also implements Comparable so once the string has been parsed you let LocalDateTime do the comparison
public class MyTimes implements Comparable<MyTimes> {
private final DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT;
//other code
public int compareTo(MyTimes o) {
LocalDateTime thisDate = LocalDateTime.from(dtf.parse(this.getStartTime()));
LocalDateTime otherDate = LocalDateTime.from(dtf.parse(o.getStartTime()));
return thisDate.compareTo(otherDate);
}
}
You can also create a separate class as a comparator if this comparison is special and what you not always want to use
public class MyTimesComparator implements Comparator<MyTimes> {
#Override
public int compare(MyTimes arg0, MyTimes arg1) {
DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT;
LocalDateTime thisDate = LocalDateTime.from(dtf.parse(this.getStartTime()));
LocalDateTime otherDate = LocalDateTime.from(dtf.parse(o.getStartTime()));
return thisDate.compareTo(otherDate);
}
}
and then use it like
someList.sort(new MyTimesComparator());
or use an inline function (I am using Instant here)
someList.sort( (m1, m2) -> {
DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT;
Instant instant1 = Instant.from(dtf.parse(m1.getStartTime));
Instant instant2 = Instant.from(dtf.parse(m2.getStartTime));
return intant1.compareTo(instant2);
});
I noticed now that you have an array and not a list so you need to convert to a list or use Arrays.sort instead.

Why can LocalDateTime return the instance?

LocalDateTime is abstract class. So I cannot write:
LocalDateTime value = new LocalDateTime(); //error
If I want to get its instance, I have to write:
LocalDateTime value = LocalDateTime.now(); //not error
I have a question, Why can LocalDateTime return the instance? It's an abstract class.
I saw the overview, but I could not find it...
LocalDateTime is not an abstract class.
public final class LocalDateTime
implements Temporal, TemporalAdjuster, ChronoLocalDateTime<LocalDate>, Serializable {
It has private constructors, so direct instantiation is not possible. Factory method such now(), now(ZoneId) etc are used to create instances.
LocalDateTime is an immutable date-time object that represents a date-time.
This class does not store or represent a time-zone. Instead, it is a description of the date. It cannot represent an instant on the time-line without additional information such as an offset or time-zone.
Hence it has static methods e.g.
LocalDateTime desc = LocalDateTime.now();

Java template function

I have a function that sometimes has to return a Date other times a DateTime (Joda-Time).
static public <T extends Object> T convertTimeForServer(DateTime toSave) {
DateTime temp = null;
try {
temp = toSave.withZone(DateTimeZone.forID(getServerTimeZone()));
} catch (Exception e) {
}
T toReturn = null;
if (toReturn.getClass().equals(temp)) {
return (T) temp;//Return DATETIME
} else {
return (T) temp.toDate();//Return DATE
}
}
Is it the right approach?
How to use it?
like this (timerHelper is the name of class):
DateTime t = timerHelper.<DateTime>convertTimeForServer(new DateTime());
Date t2 = timerHelper.<Date>convertTimeForServer(new DateTime());
or
DateTime t = (DateTime)timerHelper.convertTimeForServer(new DateTime());
Date t2 = (Date)timerHelper.convertTimeForServer(new DateTime());
And how to use this function instead?
static public <T extends Object> T current_Moment(){
return convertTimeForServer(new DateTime());
}
I suspect you're being too clever trying to use generics here. Because you don't have polymorphism on return types doesn't mean you should resort to generics to try and achieve that effect.
You can implement this simply as two methods: public static Date convertToDateForServer(DateTime toSave) {...} and public static DateTime convertToDateTimeForServer(DateTime toSave) {...}. The calling code seems to know what it wants, so it can simply call the method needed. If there really is a complex commonality to both methods, make a private method that both can call internally.
If Java 8 is available you could always implement an Either using the new Optional class.
This is one of the tricky areas of Generics. The only way to get this to work would be to take a Class argument, so the method knows what type of object to create. It can't know at the moment, because of Type Erasure.
Alternatively (much simpler) is to always return DateTime and do away with generics here.
The client will always know what it wants, and if the client wants a Date, it can create one from the DateTime far more easily than what you are trying to do.
Example:
Client 1 wants a DateTime:
DateTime result = service.convertTimeForServer(dt);
Client 2 wants a Date:
Date result = service.convertTimeForServer(dt).toDate();

A method to return 3 formats of data

I have Date in this format 2009-09-17T00:00:00.000-35:00 . As per the business Rules for my Application , i have written 3 Methods which will accept this Date and returns the Date in MM/yyyy , yyyyMM and dd .
For example one method is shown below MM/yyyy
private String getMonthYear(String date) throws Exception {
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US);
String s1 = date;
String s2 = null;
Date d;
try {
d = sdf.parse(s1);
s2 = (new SimpleDateFormat("MM/yyyy")).format(d);
} catch (ParseException e) {
e.printStackTrace();
}
return s2;
}
Similarly i have other two methods which will return data in yyyyMM and dd formats ??
This works fine , but does not look good
My question is can we have only one utility which satisfies my requirement ??
My question is can we have only one utility which satisfies my requirement ??
I think you're going about this the wrong way to start with. Fundamentally the data is just a date. You can apply formats later, when you need to. I suggest you start using Joda Time and make your method return a LocalDate. That captures all the real information, and you can then have three separate DateTimeFormatter objects used to format the value whenever you want.
Wherever you can, represent data using a type which most naturally represents the real information. Get your data into that natural format as early as possible, and keep it in that format until you have to convert it into something else (such as a string).
You could define a single method and receive as a parameter the string with the expected date format, the three strings with the formats could be defined as constants.
Yes, you could group the three methods together, and use an additional argument (an enum, for example) to specify which kind of output format you want. But I would not do that. Your solution is cleaner. Why do you think it doesn't look good?
What I would do, however, is transforming the String to a Date once and for all, and using a Date everywhere rather than the String, and transforming the Date with one of your 3 methods (which would take a Date as argument rather than a String) when needed.
The Apache Commons Lang library already has utility methods to do this for you.
For example:
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;
///
// first, convert the string to a date object
Date parsedDate = DateUtils.parseDate("2012-05-25T16:10:30.000",
new String[] {"yyyy-MM-dd'T'HH:mm:ss.SSS"});
// now, format the date object to a string, using different formats
String formattedDate = DateFormatUtils.format(parsedDate, "MM/yyyy");
String formattedDate2 = DateFormatUtils.format(parsedDate, "yyyyMM");
Take a look at DateFormatUtils and DateUtils for more information.
You could just have a Date class which has the three methods. Like below:
public class MyDate {
private String date = null;
public MyDate(String date) {
this.date = date;
}
public String getMonthYear() {
return null;
}
public String getYearMonth() {
return null;
}
public String getDay() {
return null;
}
}
You can format the String into three different Strings in the constructor and just return those strings on method calls. That implementation would be good if you make numberous/repeated calls on the same date string. Or you could format the string in the method call, if you are doing it once but if you are doing it once you may want to make the class/methods static and get rid of the constructor.

Categories