Как десериализовать массив Joda-Time LocalDate в java.time LocalDate? - PullRequest
0 голосов
/ 29 сентября 2018

После недавней миграции на Java 10 мы также рассматриваем возможность замены Joda-Time на java.time классов в нашей кодовой базе.Большим препятствием является обработка существующих сериализованных Java-объектов, которые имеют массив Joda-Time (например, LocalDate, Period) в качестве полей.

Версия Joda-Time, используемая в проекте, является настроенной, т.е.у нас есть полный контроль над этим.Я попытался обеспечить реализацию readObject в Joda-Time LocalDate, чтобы она возвращала Java LocalDate.Но это не работает, если LocalDates содержатся в массиве, где ArrayStoreException выбрасывается из readArray метода ObjectInputStream, поскольку локальная версия LocalDate является Java.

// code taken from ObjectInputStream
Object array = null;
    Class<?> cl, ccl = null;
    if ((cl = desc.forClass()) != null) {
        ccl = cl.getComponentType(); // ccl is java LocalDate.class
        array = Array.newInstance(ccl, len);
}

...// intermediate code ignored

Object[] oa = (Object[]) array;
for (int i = 0; i < len; i++) {
    oa[i] = readObject0(false); // ArrayStoreException thrown here, readObject0 returns a joda LocalDate
    handles.markDependency(arrayHandle, passHandle);
}

Я даже пытался создать подкласс ObjectInputStream, чтобы разрешить его преобразование в Java LocalDate.class в случае десериализации joda LocalDate.Это не работает, так как JVM обнаруживает, что сериализованный LocalDate не имеет тот же serialVersionUID с разрешенным (очевидно, верно, поскольку Java LocalDate имеет другой UID).В прямом намерении прорваться, я попытался сделать так, чтобы оба возвращали один и тот же UID, отражая и переопределяя readClassDescriptor из ObjectInputStream.Но позже появились новые исключения.И что усложняет то, что сериализация времени Java выполняется классом Ser.class Externalizable способом, отличным от времени joda.И ObjectInputStream обрабатывает их по-разному.Так что теперь я застрял в этом.

Прежде чем делать больше взлома в подклассе ObjectInputStream, я хочу услышать от большего количества людей о ваших мыслях / опыте относительно такой проблемы, или технически, как десериализовать измененную версию объекта массива.Кстати, я сильно сомневаюсь, что я правильно решу проблему, с которой сталкиваюсь, и смогу прочитать существующие сериализованные объекты.

Пример кода:

import org.joda.time.LocalDate;

import java.io.Serializable;
import java.util.Arrays;

public class DemoObject implements Serializable {

    private static final long serialVersionUID = 1L;

    private LocalDate[] dates;

    public DemoObject(LocalDate[] dates) {
        this.dates = dates;
    }

    @Override
    public String toString() {
        return Arrays.asList(dates).toString();
    }
}

///////////////////////////////////////////////////////////////////

import org.joda.time.LocalDate;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.List;

public class SerializationDemo {

    public static void main(String[] args) throws Exception {
        LocalDate[] dates = new LocalDate[2];
        for (int i=0; i<dates.length; i++) {
            dates[i] = new LocalDate().minusDays(i);
        }

        DemoObject demoObject = new DemoObject(dates);
        serialize(demoObject);
        deserialize(getSerialFileName(demoObject));
    }

    private static void serialize(Object demoObject) throws IOException {
        try(ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(getSerialFileName(demoObject)))) {
            out.writeObject(demoObject);
        }
    }

    private static Object deserialize(String fileName) throws IOException, ClassNotFoundException {
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(fileName))) {
            Object demoObject = in.readObject();
            System.out.println(demoObject);
            return demoObject;
        }
    }
}

Первый запускприведенный выше код для создания файла ser.Затем прокомментируйте вызов метода serialize(demoObject); и замените joda LocalDate на java LocalDate.Запустите снова код, который вы получите ClassCastException, сообщая, что он пытается преобразовать org.joda.time.LocalDate в java.time.LocalDate.Мы можем обойти это путем создания подкласса ObjectInputStream и переопределить метод readObject для возврата java LocalDate в случае десериализации joda.

public class DemoObjectInputStream extends ObjectInputStream {

    public DemoObjectInputStream(InputStream in) throws IOException {
        super(in);
        enableResolveObject(true);
    }

    @Override
    protected final Object resolveObject(Object object) {
        if (object != null && object.getClass().equals(org.joda.time.LocalDate.class)) {
            org.joda.time.LocalDate date = (org.joda.time.LocalDate) object;
            return LocalDate.of(date.getYear(), date.getMonthOfYear(), date.getDayOfMonth());
        }
        return object;
    }
}

Однако при создании объекта десериализованного массива будет выдано исключение ArrayStoreException.

Exception in thread "main" java.lang.ArrayStoreException: java.time.LocalDate
        at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1707)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1345)
        at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2000)
        at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1924)
        at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1801)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
        at com.serialization.SerializationDemo.deserialize(SerializationDemo.java:51)
        at com.serialization.SerializationDemo.main(SerializationDemo.java:40)

Как обойти такую ​​проблему с массивом?

...