Java 序列化可以方便地存储 Java 对象的状态。但是,序列化数据存在一些缺点:
-
它不是人类可读的。
-
它是特定于Java的,不能与其他编程语言交换。
-
如果关联的 Java 类的字段已更改,则无法迁移。
这些缺点使得 Java 序列化不是为实际项目存储对象状态的实用方法。在最近开发的产品中,我们使用 XStream 序列化/反序列化 Java 对象,这解决了第一个和第二个问题。第三个问题通过XMT得到解决,XMT是我们开发的用于迁移XStream序列化XL的开源工具。本文通过一些示例介绍此工具。
计算机语言需要简化
当我们努力将计算机语言转换为人类可以更好地理解的东西时,我们都会遇到很多问题,因为如果可能的话,计算机语言需要简化。
这些语言非常适合彼此来回交谈的计算机,但是当人类试图参与其中时,它们不一定也能正常工作。许多人最终感到困惑,无法在清理这些系统方面取得多大进展。因此,有必要清理它们并使其更易于使用。现在有些人正在积极地解决这个问题,但与此同时,我们可能只需要处理那些无法完成我们希望他们做的所有事情的计算机。
类演进时的 XStream 反序列化问题
假设下面的 Task 类具有优先级字段,指示它是否为按优先级排列的任务:
package example;
public class Task {
public boolean prioritized;
}
使用 XStream,我们可以将此类的对象序列化为 XML,如下所示:
import com.thoughtworks.xstream.XStream;
public class Test {
public static void main(String args[]) {
Task task = new Task();
task.prioritized = true;
String xml = new XStream().toXML(task);
saveXMLToFileOrDatabase(xml);
}
private static void saveXMLToFileOrDatabase(String xml) {
// save XML to file or database here
}
}
生成的 XML 将为:
<example.Task> <prioritized>true</prioritized> </example.Task>
您可以反序列化 XML 以取回任务对象:
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class Test {
public static void main(String args[]) {
String xml = readXMLFromFileOrDatabase();
Task task = (Task) new XStream(new DomDriver()).fromXML(xml);
}
private static String readXMLFromFileOrDatabase() {
// read XML from file or database here
}
}
一切都很好。现在我们发现优先级标志是不够的,因此我们增强了 Task 类,以便能够区分高优先级、中优先级和低优先级:
package example;
public class Task {
enum Priority {HIGH, MEDIUM, LOW}
public Priority priority;
}
但是,由于新的 Task 类与以前的版本不兼容,因此无法再对以前保存的 XML 进行反序列化。
XMT 如何解决问题
XMT来拯救:它将类VersionedDocument引入版本序列化XML并处理迁移。使用 XMT,任务对象的序列化可以写为:
package example;
import com.pmease.commons.xmt.VersionedDocument;
public class Test {
public static void main(String args[]) {
Task task = new Task();
task.prioritized = true;
String xml = VersionedDocument.fromBean(task).toXML();
saveXMLToFileOrDatabase(xml);
}
private static void saveXMLToFileOrDatabase(String xml) {
// save XML to file or database here
}
}
For task class of the old version, the resulting XML will be:
<example.Task version="0"> <prioritized>true</prioritized> </example.Task>
Compared with the XML generated previously with XStream, an additional attribute version is added to the root element indicating the version of the XML. The value is set to "0" unless there are migration methods defined in the class as we will introduce below.
When Task class is evolved to use enum based priority field, we add a migrated method like the below:
package example;
import java.util.Stack;
import org.dom4j.Element;
import com.pmease.commons.xmt.VersionedDocument;
public class Task {
enum Priority {HIGH, MEDIUM, LOW}
public Priority priority;
@SuppressWarnings("unused")
private void migrate1(VersionedDocument dom, Stack<Integer> versions) {
Element element = dom.getRootElement().element("prioritized");
element.setName("priority");
if (element.getText().equals("true"))
element.setText("HIGH");
else
element.setText("LOW");
}
}
迁移方法需要声明为私有方法,其名称为“migrateXXX”,其中“XXX”是指示类的当前版本的数字。此处的方法“migrate1”指示 Task 类的当前版本为“1”,并且该方法将 XML 从版本“0”迁移到“1”。要迁移的 XML 作为版本化文档对象传递,该对象实现 dom4j 文档接口,您可以使用 dom4j 迁移它以与该类的当前版本兼容。
在这种迁移方法中,我们读回版本“0”的“优先级”元素,将其重命名为“优先级”,如果任务最初是优先级任务,则将值设置为“HIGH”;否则,将值设置为“低”。
定义此迁移方法后,您现在可以安全地从 XML 反序列化任务对象:
package example;
import com.pmease.commons.xmt.VersionedDocument;
public class Test {
public static void main(String args[]) {
String xml = readXMLFromFileOrDatabase();
Task task = (Task) VersionedDocument.fromXML(xml).toBean();
}
private static String readXMLFromFileOrDatabase() {
// read XML from file or database here
}
}
反序列化不仅适用于旧版本的 XML,也适用于新版本的 XML。在反序列化时,XMT 将 XML 版本(如前所述记录在 version 属性中)与类的当前版本(各种迁移方法的最大后缀数)进行比较,并逐个运行适用的迁移方法。在这种情况下,如果读取版本“0”的XML,则将调用方法 migrate1;如果读取版本“1”的 XML,则不会调用任何迁移方法,因为它已经是最新的。
随着类的不断发展,可以通过增加最新迁移方法的后缀号来向类添加更多迁移方法。例如,让我们进一步增强 Task 类,以便优先级字段采用从“1”到“10”的数值。我们将另一个迁移方法添加到 Task 类以接受更改:
@SuppressWarnings("unused")
private void migrate2(VersionedDocument dom, Stack<Integer> versions) {
Element element = dom.getRootElement().element("priority");
if (element.getText().equals("HIGH"))
element.setText("10");
else if (element.getText().equals("MEDIUM"))
element.setText("5");
else
element.setText("1");
}
此方法仅处理从版本“1”到版本“2”的迁移,我们不再需要关心版本“0”,因为在运行此方法之前,版本“0”的XML将首先通过调用方法transport1迁移到版本“1”。
通过此更改,您将能够从当前版本和任何早期版本的 XML 反序列化任务对象。
本文演示了如何迁移类的字段更改的想法。XMT可以处理许多复杂的场景,例如迁移在多层类层次结构中定义的数据,解决类层次结构更改等。
来源:Migrate Serialized Java Objects with XStream and XMT - DZone Java
本文介绍了在Java开发中遇到的序列化问题,特别是类演进时的反序列化挑战。XStream作为一种序列化库解决了部分问题,但无法处理类的不兼容变化。为了解决这个问题,文章提出了开源工具XMT,它通过添加版本信息和迁移方法,实现了对XStream序列化数据的兼容性迁移。通过XMT,开发者可以安全地反序列化不同版本的类对象。
1万+

被折叠的 条评论
为什么被折叠?



