Обработка десериализации при изменении типа данных поля класса

У меня есть сериализуемый класс.

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) также является альтернативой.

Другие вопросы по тегам