【问题标题】:Handle deserialization when datatype of class field changed当类字段的数据类型改变时处理反序列化
【发布时间】:2016-01-13 12:59:55
【问题描述】:

我有一个可序列化的类。

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);
    }


}

我已将类的对象序列化为一个文件。现在我已将名称的数据类型从 String 更改为 List。因此,在反序列化时,我得到了一个类转换异常,因为它无法从 String 转换为 List。 每次对类进行一些更改时,我都在考虑更改类的版本,以便在 readExternal 中我可以明确地处理它。然而,虽然这个想法可能适用于简单的类,但在较大的复杂类的情况下会失败。谁能提供一个更简单的解决方案。

谢谢

【问题讨论】:

  • 如果你有一个包含String的序列化对象,你希望它在反序列化时如何自动转换为List
  • 我没有,但可以通过更改 readExternal 方法来显式处理。在此处找到链接stackoverflow.com/questions/3678136/…。这适用于小型简单类,但我有一个包含许多内部类的复杂类。所以需要一个更简单可行的解决方案。
  • 如果你经常换班,那是一种误解。如果它是一次性迁移,那么没有比明确处理它更好的解决方案了。

标签: java serialization deserialization externalizable


【解决方案1】:

您只需要自己管理不同的可能性(并执行适当的转换)。

@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");
    }
  }
}

【讨论】:

    【解决方案2】:

    我建议你看看不同的序列化引擎,例如Protocol BuffersApache AvroApache Thrift

    其他可能性:使用策略模式来选择序列化算法并在readExternal/writeExternal 上委托给它。但是,您仍然需要一个“选择器”。类标识符(全名?)和版本通常是首选,但序列化布局(即String+StringString+List)也是一种替代方案。

    【讨论】:

      【解决方案3】:

      您最好实现一个迁移工具,将序列化对象从一个版本转换为另一个版本(通过将其反序列化为旧类的实例,然后创建新类的实例并复制字段)。保持简单,不需要过于聪明的代码。

      我还建议不要在您的 readExternal 方法中实现迁移算法,以便更好地分离关注点,更不用说最有可能提高性能了,因为您可以而且应该省略 readExternal,因为反序列化提供程序通常正在执行做得很好,除非您不一遍又一遍地反序列化相同的旧对象,否则分支(是版本 x 还是 y?)只会产生一次“旧”分支,但会针对每次反序列化进行评估。最后但同样重要的是:您没有的代码是您不需要维护的代码 -> 提高了可维护性。

      顺便说一句:serialVersionUID 字段的想法是给反序列化实现一个提示,即序列化对象不能反序列化为同一类的实例,因为它已更改(因此使其成为具有相同名称的不同类) .如果您在进行更改时不更改版本字段,它完全没用,您可以将其设置为 0 或任何常量,并且永远不要触摸它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-07-29
        • 1970-01-01
        相关资源
        最近更新 更多