Как десериализовать объект Java с различными идентификаторами serialVersionUID?

Представьте, что у меня есть класс Foo, у которого в прошлом были serialVersionUIDs 1, 3 и 17, и мне нужно иметь возможность читать все три версии в Foo экземплярах. Как мне это сделать?

Мне особенно нужно знать, какая версия была сохранена в потоке байтов, чтобы я мог выполнить миграцию данных. Как мне получить доступ к serialVersionUID в readObject()? Или это уже слишком поздно?


person Aaron Digulla    schedule 06.02.2013    source источник
comment
Взгляните на это: javablogging.com/what-is-serialversionuid   -  person Christophe Roussy    schedule 06.02.2013


Ответы (3)


Используйте разные загрузчики классов для десериализации разных версий объекта. После этого вам нужно вручную инициализировать внутреннее состояние общего (возможно, самого последнего) экземпляра на основе предыдущих версий (для этого вы должны написать конвертер).

Убедитесь, что ваш путь к загрузчику классов полностью отличается. Если суперзагрузчик классов может загрузить класс, он загрузится. Таким образом, все загрузчики классов должны быть полностью отделены друг от друга (ни один не должен быть супер загрузчиком других).

person gaborsch    schedule 06.02.2013

Это непросто, но возможно. Для каждой исторической версии класса вам нужно создать отдельный загрузчик классов, который будет загружать старые версии классов модели данных. Затем вам нужно попробовать прочитать ваш файл с каждым загрузчиком классов, пока некоторые из них не подойдут. Я считаю, что ObjectInputStream использует текущий загрузчик классов из контекста потока. Не стесняйтесь спрашивать меня, если вам нужна дополнительная информация.

Более позднее дополнение: кажется, не так просто указать, какой загрузчик классов будет использоваться ObjectInputStream. Здесь, вероятно, необходимы дополнительные исследования.

Еще одно более позднее дополнение: возможно, вы можете переопределить метод resolveClass(ObjectStreamClass) в ObjectInputStream, чтобы использовать правильный загрузчик классов.

person Mikhail Vladimirov    schedule 06.02.2013
comment
Вероятно, мне придется переопределить readClassDescriptor() :-/ См. этот ответ stackoverflow.com/a/1816711/34088 - person Aaron Digulla; 06.02.2013

Вы можете получить прежний serialVersionUID, перехватив ошибку во время десериализации:

try {
  // deserialize here...
} catch (final java.io.InvalidClassException e) {
  System.out.println(e.getMessage());
  // The message string contains the former uid.
  // Message: local class incompatible: stream classdesc serialVersionUID = OLD_UID_HERE, local class serialVersionUID = NEW_UID_HERE
}
person Christophe Roussy    schedule 06.02.2013
comment
Хорошо, но как он тогда получит данные? - person gaborsch; 06.02.2013
comment
Ну что, он мог разобрать сообщение? По крайней мере, uid не теряется, и он может сделать какую-то процедуру миграции на его основе. - person Christophe Roussy; 06.02.2013
comment
Конечно, он не будет. Но он может проверить старый источник и сделать миграцию (вы отвечаете на пункт 1 его вопроса), я отвечаю только на часть о том, как найти старые uids. - person Christophe Roussy; 06.02.2013
comment
Спасибо :) ваше решение может работать, если вообще нет изменений в структуре данных и сериализация будет проходить до некоторой фазы, когда объект готов, но отличается только uid. Но я думаю, что десериализация немедленно прекращается, если iud отличается. - person gaborsch; 06.02.2013
comment
Он остановится, но с упомянутым исключением. Но это первый шаг для получения старого uid и принятия решения (используйте соответствующий загрузчик классов..., не уверен в этой части) - person Christophe Roussy; 06.02.2013
comment
Да, вы можете принимать решения по этому сообщению, даже пойти и попробовать конкретный загрузчик классов (вам не обязательно пробовать их все). +1 за это - person gaborsch; 06.02.2013