Обработка десериализации при изменении типа данных поля класса
У меня есть сериализуемый класс.
public class Customer implements Externalizable {
private static final long serialVersionUID = 1L;
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName( String name) {
this.name = name;
}
@Override
public String toString() {
return "id : "+id+" name : "+name ;
}
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.setId((String) in.readObject());
this.setName((String) in.readObject());
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
System.out.println("Reached here");
out.writeObject(id);
out.writeObject(name);
}
}
Я сериализовал объект класса в файл. Теперь я изменил тип данных name с String на List. Поэтому при десериализации я получаю исключение приведения класса, потому что он не может преобразовать строку в список. Я думал об изменении версии класса каждый раз, когда в класс вносятся какие-то изменения, чтобы в readExternal я мог обработать это явно. Однако, хотя эта идея может работать для простых классов, она потерпит неудачу в случае более сложных классов. Может кто-нибудь, пожалуйста, предоставьте более простое решение для этого.
Спасибо
3 ответа
Вы просто должны управлять различными возможностями (и выполнить соответствующее преобразование) самостоятельно.
@Override
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.setId((String) in.readObject());
Object nameField = in.readObject();
if (nameField != null) {
boolean resolved = false;
if (nameField instanceof String) {
ArrayList<String> list = new ArrayList<String>(); // Or whatever you want to for converting the String to list.
list.add((String)nameField);
this.setName(list);
resolved = true;
}
if (nameField instanceof List) {
this.setName((List<String>) nameField);
resolved = true;
}
if (!resolved) {
throw new Exception("Could not deserialize " + nameField + " into name attribute");
}
}
}
Лучше всего реализовать инструмент миграции для преобразования сериализованных объектов из одной версии в другую (путем десериализации его в экземпляр старого класса, а затем создания экземпляра нового класса и копирования полей). Сохраняйте это простым, не нужно слишком умный код.
Я также рекомендую не реализовывать алгоритм миграции в вашем readExternal
метод для лучшего разделения проблем, не говоря уже о наиболее вероятном улучшении производительности, потому что вы можете и должны опустить readExternal
поскольку провайдер десериализации, как правило, выполняет довольно хорошую работу, и если вы не десериализовываете один и тот же старый объект снова и снова, ветвь (это версия x или y?) выдаст только один раз "старой" ветке, но будет оценена для каждая десериализация. И последнее, но не менее важное: код, которого у вас нет, - это код, который вам не нужно поддерживать -> улучшенная поддержка.
Кстати, идея поля serialVersionUID состоит в том, чтобы дать реализации десериализации намек на то, что сериализованный объект не может быть десериализован для экземпляра того же класса, потому что он изменился (что делает его другим классом с тем же именем). Если вы не меняете поле версии при внесении изменений, оно совершенно бесполезно, и вы можете установить его в 0 или что-нибудь постоянное и никогда не трогать его.
Я предлагаю вам взглянуть на разные движки сериализации, такие как Protocol Buffers, Apache Avro или Apache Thrift.
Другие возможности: используйте шаблон стратегии, чтобы выбрать алгоритм сериализации и делегировать его на readExternal
/writeExternal
, Однако вам все еще нужен "селектор". Идентификатор класса (полное имя?) И версия, как правило, лучшие кандидаты, но макет сериализации (то есть String
+String
ОУ String
+List
) также является альтернативой.