Как сериализовать Java ZonedDateTime в файл XML - PullRequest
0 голосов
/ 12 ноября 2019

Ошибка при сериализации ZonedDateTime (она вообще не отображается в выходном xml):

java.lang.InstantiationException: java.time.ZonedDateTime

Продолжение ...

java.lang.RuntimeException: не удалось оценить: = Class.new ();

Продолжение ...

У меня есть экземпляр класса, где одно из полейимеет тип ZonedDateTime. Когда я пытаюсь сериализовать объект с помощью XMLEncoder:

import java.beans.XMLEncoder;

я получаю эти ошибки. В выходном файле отображаются все остальные поля, кроме поля с ZonedDateTime

это поле с ZonedDateTime выглядит следующим образом, например:

ZonedDateTime date = ZonedDateTime.parse("2010-01-10T00:00:00Z[CET]");

Есть ли способ преобразовать его в такой формат даты, которыйэто будет работать? например,

ZonedDateTime.parse("2010-01-10T00:00:00").toLocalDateTime().atZone(ZoneId.of("CET")

Выше написанное (с .toLocalDateTime ()) может не иметь никакого смысла, но это только пример.

Я фактически сериализирую весь список этих объектов,поэтому ошибка появляется много раз (и всегда не выводится в XML-файл)

1 Ответ

1 голос
/ 12 ноября 2019

XMLEncoder и XMLDecoder предназначены для работы с обычными классами Java-бинов. Как правило, это классы, которые имеют открытый конструктор с нулевым аргументом и методы доступа к общедоступным свойствам. Существует некоторая поддержка других классов, таких как классы с конструкторами, которые принимают значения свойств, но большинство классов java.time отличаются и не имеют встроенной поддержки для них.

К счастью, вы можете предоставить свой собственный* путем поддержки указанием PersistenceDelegate для каждого класса не-Java-компонента, который вы планируете сериализовать.

Итак, первым шагом является предоставление PersistenceDelegate для 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);

Но оказывается, что этого недостаточно, потому что части ZonedDateTime также сериализуются, и одна из них - это ZoneId. Таким образом, нам также нужен PersistenceDelegate для ZoneId.

То, что PersistenceDelegate легко написать:

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() });
    }
};

Но зарегистрировать его не так просто. encoder.setPersistenceDelegate(ZoneId.class, zoneIdDelegate); не будет работать, потому что ZoneId является абстрактным классом, что означает, что в нем нет объектов ZoneId, только экземпляры подклассов. XMLEncoder не проверяет наследование при проверке на PersistenceDelegates. Должен быть элемент PersistenceDelegate для каждого класса каждого объекта, подлежащего сериализации.

Если вы сериализуете только один ZonedDateTime, решение легко:

encoder.setPersistenceDelegate(
    date.getZone().getClass(), zoneIdDelegate);

Если у вас есть коллекцияих, вы можете проверить все их классы ZoneId:

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);
    }
}

Если у вас есть агрегатные объекты, содержащие ZonedDateTimes, вы можете просто перебрать их аналогичным образом и получить доступ к этим значениям ZonedDateTime.

...