0

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)

emsiiggy
  • 343
  • 1
  • 2
  • 13
  • `"2010-01-10T00:00:00"` isn't a `ZonedDateTime`. It has no time zone, so you would have to parse it as a `LocalDateTime` directly. – Andy Turner Nov 11 '19 at 22:58
  • Why not `LocalDateTime.parse(yourString).atZone(ZoneId.of("CET"))`? – Andy Turner Nov 11 '19 at 22:59
  • 1
    Do you get the error during serializing or deserializing? Your error shows that it can't instantiate a ZonedDateTime using it's constructor (which is correct - it needs to call the `parse` method, and you possibly need some type of adapter for your XML parser for that). But you wouldn't get that error during **ser**ializing, as it doesn't *need* to create instances during serialization. – Erwin Bolwidt Nov 11 '19 at 23:11
  • @ErwinBolwidt I have this issue when trying to serialize (that is write to output.xml file from program). – emsiiggy Nov 12 '19 at 08:42
  • @AndyTurner so you mean something like this: ZonedDateTime date = LocalDateTime.parse(DateInString).atZone(ZoneId.of("CET")) ? I think i tried it already, but will try it again – emsiiggy Nov 12 '19 at 08:42
  • Are you serializing ZonedDateTime objects directly, or are you serializing aggregate objects with one or more ZonedDateTime properties? If it’s the former, you may find it easier to serialize them as strings and parse those strings when deserializing. – VGR Nov 12 '19 at 12:45
  • Please post a [mcve] that reproduces your problem. Without that, it's just guessing. – Erwin Bolwidt Nov 12 '19 at 21:37

1 Answers1

1

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.

VGR
  • 40,506
  • 4
  • 48
  • 63
  • Okay, and how can i decode this when getting info from file to program? – emsiiggy Nov 14 '19 at 11:41
  • Just use [XMLDecoder](https://docs.oracle.com/en/java/javase/13/docs/api/java.desktop/java/beans/XMLDecoder.html). No customization is necessary. – VGR Nov 14 '19 at 12:19