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.