class 字段的数据类型更改时处理反序列化
Handle deserialization when datatype of class field changed
我有一个可序列化的 class。
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);
}
}
我已经将 class 的对象序列化到一个文件中。现在我已经将名称的数据类型从字符串更改为列表。因此,在反序列化时,我得到了一个 class 转换异常,因为它无法从 String 转换为 List。
每次对 class 进行某些更改时,我都在考虑更改 class 的版本,以便在 readExternal 中我可以明确地处理它。然而,虽然这个想法可能适用于简单的 classes,但它在更大的复杂 classes 的情况下会失败。谁能为此提供一个更简单的解决方案。
谢谢
您只需要自己管理不同的可能性(并执行适当的转换)。
@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");
}
}
}
我建议你看看不同的序列化引擎,比如Protocol Buffers, Apache Avro or Apache Thrift。
其他可能性:使用策略模式 select 序列化算法并在 readExternal
/writeExternal
上委托给它。但是你仍然需要一个"selector"。 Class 标识符(全名?)和版本通常是首选,但序列化布局(即 String
+String
或 String
+List
)也是备选方案.
您最好实现一个迁移工具,用于将序列化对象从一个版本转换为另一个版本(通过将其反序列化为旧 class 的实例,然后创建新 class 的实例并复制田野)。保持简单,不需要过于聪明的代码。
我还建议不要在您的 readExternal
方法中实施迁移算法,以便更好地分离关注点,更不用说最有可能提高的性能,因为您可以而且应该省略 readExternal
反序列化提供程序通常做得很好,除非您不一遍又一遍地反序列化同一个旧对象,否则分支(是版本 x 还是版本 y?)只会对 "old" 分支产生一次,但会评估每次反序列化。最后但同样重要的是:你没有的代码是你不需要维护的代码 -> 提高了可维护性。
顺便说一句:serialVersionUID 字段的想法是给反序列化实现一个提示,即序列化对象不能反序列化为相同 class 的实例,因为它已更改(因此使其成为不同的class 同名)。如果你在修改的时候不修改version字段,那是完全没有用的,你可以把它设置为0或者任何常量,永远不要碰它。
我有一个可序列化的 class。
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);
}
}
我已经将 class 的对象序列化到一个文件中。现在我已经将名称的数据类型从字符串更改为列表。因此,在反序列化时,我得到了一个 class 转换异常,因为它无法从 String 转换为 List。 每次对 class 进行某些更改时,我都在考虑更改 class 的版本,以便在 readExternal 中我可以明确地处理它。然而,虽然这个想法可能适用于简单的 classes,但它在更大的复杂 classes 的情况下会失败。谁能为此提供一个更简单的解决方案。
谢谢
您只需要自己管理不同的可能性(并执行适当的转换)。
@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");
}
}
}
我建议你看看不同的序列化引擎,比如Protocol Buffers, Apache Avro or Apache Thrift。
其他可能性:使用策略模式 select 序列化算法并在 readExternal
/writeExternal
上委托给它。但是你仍然需要一个"selector"。 Class 标识符(全名?)和版本通常是首选,但序列化布局(即 String
+String
或 String
+List
)也是备选方案.
您最好实现一个迁移工具,用于将序列化对象从一个版本转换为另一个版本(通过将其反序列化为旧 class 的实例,然后创建新 class 的实例并复制田野)。保持简单,不需要过于聪明的代码。
我还建议不要在您的 readExternal
方法中实施迁移算法,以便更好地分离关注点,更不用说最有可能提高的性能,因为您可以而且应该省略 readExternal
反序列化提供程序通常做得很好,除非您不一遍又一遍地反序列化同一个旧对象,否则分支(是版本 x 还是版本 y?)只会对 "old" 分支产生一次,但会评估每次反序列化。最后但同样重要的是:你没有的代码是你不需要维护的代码 -> 提高了可维护性。
顺便说一句:serialVersionUID 字段的想法是给反序列化实现一个提示,即序列化对象不能反序列化为相同 class 的实例,因为它已更改(因此使其成为不同的class 同名)。如果你在修改的时候不修改version字段,那是完全没有用的,你可以把它设置为0或者任何常量,永远不要碰它。