После недавней миграции на 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)
Как обойти такую проблему с массивом?