Getting Started

You are viewing an old version (v. 8) of this page.
The latest version is v. 25, last edited on Feb 18, 2010 (view differences | )
<< View previous version | view page history | view next version >>

We demonstrate usage of this tool with a simple tutorial. Assume a Task class below with a prioritized field indicating whether it is a prioritized task:

package example;

public class Task {
        public boolean prioritized;
}

With XStream, we can serialize object of this class to XML like below:

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

The resulting XML will be:

<example.Task>
  <prioritized>true</prioritized>
</example.Task>

And you can deserialize the XML to get back task object:

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

Everything is fine. Now we find a prioritized flag is not enough, so we enhance the Task class to use a numeric priority field ranging from 1 to 10:

package example;

public class Task {
        public int priority;
}

However deserialization of previously saved xml is no longer possible since the new Task class is not compatible with previous version. XMT comes to rescue: it introduces class VersionedDocument to version serialized XMLs and handles the migration. With XMT, serialization of task object can be written as:

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

The resulting XML will be:

<?xml version="1.0" encoding="UTF-8"?>

<example.Task version="0">
  <prioritized>true</prioritized>
</example.Task>

Compared with the XML generated by directly using XStream, an additional attribute version is added to the root element indicating version of associated class. 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 numeric priority field, we add a migrate method like below:

package com.pmease.commons.xmt.bean;

import java.util.List;
import org.dom4j.Element;
import com.pmease.commons.xmt.VersionedDocument;

public class Task {
	private int priority;

	public int getPriority() {
		return priority;
	}

	public void setPriority(int priority) {
		this.priority = priority;
	}
	
	@SuppressWarnings("unused")
	private void migrate1(VersionedDocument dom, List<String> versions) {
		Element element = dom.getRootElement().element("prioritized");
		element.setName("priority");
		if (element.getText().equals("true"))
			element.setText("10");
		else
			element.setText("1");
	}
}

Name of all migration methods need to be in the form of migrateXXX, where XXX is a number indicating current version of the class. Here method migrate1 indicates that current version of the task class is of "1", and the method migrates the XML from version 0 to 1. As the class keeps evolving, more migration methods can be added by incrementing suffix number of previous migration method. The XML to be migrated is passed as a VersionedDocument object which implements dom4j Document interface and you may use dom4j to migrate it to be compatible with current version of the class. The versions parameter is used to handle the migration if class inheritance hierarchy is changed and will be introduced in [class hierarchy migration chapter].
In this migration method, we read back the "prioritized" element of version 0, rename it as "priority", and then set the value as "10" if the task is originally a prioritized task; otherwise, set the value as "0".

With this migration method defined, you can now safely deserialize the task object from 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();
	}
}

The deserialization works not only for XML of the old version, but also for XML of the new version. At deserialization time, XMT compares version of the XML (recorded in the version attribute as we mentioned earlier) with current version of the class (maximum suffix number of various migrate methods), and run applicable migrate methods one by one. In this case, if a xml of version "0" is read, method migrate1 will be called; if a xml of version "1" is read, no migration methods will be called since it is already up to date.

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.