九、持久化
在第八节的示例中,当我们操作一组对象时,我们可以指定Writer、OutputStream来写出序列化后的XML数据,我们还可以指定Reader、InputStream来读取序列化后的XML数据。当我们需要写出和读取文件时都需要指定输入输出流,并且需要明确的调用输入输出方法来实现Java对象的序列化和反序列化,其实我们完全可以让Java对象的序列化和反序列化操作隐性的、自动的完成,这就是我们要学的内容:PersistenceStrategy、XmlArrayList、XmlMap和XmlSet。
PersistenceStrategy是我们的持久化策略,定义了我们的存储和读取协议,是实际做存储和读取XML的工具。XStream框架提供的持久化策略只有FilePersistenceStrategy这一种,即将XML数据持久化到文件系统中,但是我们可以定义自己的持久化策略(比如持久化到数据库中),只要继承PersistenceStrategy接口就行了。
XmlArrayList、XmlMap和XmlSet是我们熟悉的3个集合工具类,它们可以让我们以我们非常熟悉的方式操作Java对象,并且隐性的存储和读取为我们需要的XML。
下面我们以XmlArrayList为例来学习。
1,简单的存储
程序如下:
package cn.tjpu.zhw.xml.xstream6;
import java.io.File;
import java.util.List;
import com.thoughtworks.xstream.persistence.FilePersistenceStrategy;
import com.thoughtworks.xstream.persistence.PersistenceStrategy;
import com.thoughtworks.xstream.persistence.XmlArrayList;
public class PersistenceMain {
public static void main(String[] args) {
//创建持久化策略(定义存储工具和存储位置)
//注:d:/tmp是一个已存在的目录,否则会报错
PersistenceStrategy strategy = new FilePersistenceStrategy(
new File("d:/tmp"));
//创建操作工具
List list = new XmlArrayList(strategy);
System.out.println("刚创建XmlArrayList对象时list.size()="+list.size());
//添加数据
list.add(new Person("张三"));
list.add(new Person("李四"));
list.add(new Person("毛毛"));
list.add(new Person("大熊"));
System.out.println("添加了4个元素之后list.size()="+list.size());
//删除“李四”
list.remove(1);
System.out.println("删除了1个元素之后list.size()="+list.size());
}
}
class Person {
public Person(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Person对象的name=" + getName();
}
}
运行结果:
刚创建XmlArrayList对象时list.size()=0
添加了4个元素之后list.size()=4
删除了1个元素之后list.size()=3
现在我们查看d:/tmp目录,我们会发现有3个文件int@0.xml、int@1.xml和int@2.xml。这3个文件就是我们存储的3个Person对象,他们的内容分别是:
int@0.xml文件:
<cn.tjpu.zhw.xml.xstream6.Person> <name>张三</name> </cn.tjpu.zhw.xml.xstream6.Person>
int@1.xml文件
<cn.tjpu.zhw.xml.xstream6.Person> <name>毛毛</name> </cn.tjpu.zhw.xml.xstream6.Person>
int@2.xml文件:
<cn.tjpu.zhw.xml.xstream6.Person> <name>大熊</name> </cn.tjpu.zhw.xml.xstream6.Person>
其实,在我们每一次调用add方法的时候,都有一次持久化过程,每次都会将文件写入到d:/tmp目录。
2,所有我们熟悉的操作方法
由于XmlArrayList、XmlMap和XmlSet继承我们熟悉各个集合接口,所以我们可以向操作List、Map和Set一样来操作我们的数据,所不同的是原来我们集合中的数据在内存中,现在却在我们预定义的持久化策略中。
编写程序如下:
package cn.tjpu.zhw.xml.xstream6;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import com.thoughtworks.xstream.persistence.FilePersistenceStrategy;
import com.thoughtworks.xstream.persistence.PersistenceStrategy;
import com.thoughtworks.xstream.persistence.XmlArrayList;
public class UsageTestMain {
// XmlArrayList的用法
public static void main(String[] args) {
// 创建持久化策略(定义存储工具和存储位置)
// 注:d:/tmp是一个已存在的目录,否则会报错
PersistenceStrategy strategy = new FilePersistenceStrategy(new File(
"d:/tmp"));
// 创建操作工具
List list = new XmlArrayList(strategy);
System.out.println("刚创建XmlArrayList对象时list.size()="+list.size());
System.out.println();
//获取迭代器
Iterator iter = list.iterator();
System.out.println("******遍历每一个元素******");
//遍历每一个元素
while(iter.hasNext()){
Person p = (Person)iter.next();
System.out.println("当前元素p="+p);
}
}
}
运行结果:
刚创建XmlArrayList对象时list.size()=3
******遍历每一个元素******
当前元素p=Person对象的name=张三
当前元素p=Person对象的name=毛毛
当前元素p=Person对象的name=大熊
3,定制自己的转换器(Local Converter)
由于内存中存储了大量数据,我们可以使用文件系统暂存,内存中只记录存放文件的目录即可,这是我们可以自己定义一个转换器:
package cn.tjpu.zhw.xml.xstream6;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.persistence.FilePersistenceStrategy;
import com.thoughtworks.xstream.persistence.XmlArrayList;
//自己定义一个局部转换器
public final class LocalArrayListConverter implements Converter {
private XStream xstream;
public LocalArrayListConverter(XStream xstream) {
this.xstream = xstream;
}
//将Collection对象序列化到文件中
//注:序列化时内存中并没有存放集合中的内容,只是暂存了这些文件存放的目录
public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
File dir = new File("d:/tmp");
//创建持久化工具,并加载目录中的所有文件
XmlArrayList list = new XmlArrayList(
new FilePersistenceStrategy(dir,xstream));
context.convertAnother(dir);
//生成文件
list.addAll((Collection) source);
}
//从文件中读取信息,反序列换为Collection对象
//注:反序列化时会删除暂存目录下的所有文件
public Object unmarshal(
HierarchicalStreamReader reader,
UnmarshallingContext context) {
File directory = (File) context.convertAnother(null, File.class);
//创建持久化工具,并加载目录中的所有文件
XmlArrayList persistentList = new XmlArrayList(
new FilePersistenceStrategy(directory, xstream));
//将已加载的信息复制一份到list中
ArrayList list = new ArrayList(persistentList);
//删除所有文件
persistentList.clear();
//返回已加载的信息
return list;
}
public boolean canConvert(Class type) {
return type == ArrayList.class;
}
}
这个转换器是转换ArrayList对象的。
下面是我们的测试程序:
package cn.tjpu.zhw.xml.xstream6;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.thoughtworks.xstream.XStream;
public class LocalConverterMain {
public static void main(String[] args) {
XStream xstream = new XStream();
xstream.alias("volume", Volume.class);
//使用自定义的转换器LocalArrayListConverter来转换Volume类的documents字段
//这个转换器是受限制的局部(local)转换器,只能转换Volume类的documents字段
xstream.registerLocalConverter(Volume.class, "documents",
new LocalArrayListConverter(xstream));
//要转换的对象
Volume volume = new Volume();
//创建集合
Collection coll = new ArrayList();
coll.add(1);
coll.add(2.123d);
coll.add(new Person("张三"));
volume.documents.addAll(coll);
System.out.println("******序列化******");
//转换XML
String xml = xstream.toXML(volume);
//输出XML
System.out.println(xml);
System.out.println("******反序列化******");
Volume v2 = (Volume)xstream.fromXML(xml);
for(Object obj:v2.documents){
System.out.println("obj="+obj);
}
}
}
abstract class AbstractDocument {
String title;
}
class TextDocument extends AbstractDocument {
List chapters = new ArrayList();
}
class ScannedDocument {
List images = new ArrayList();
}
class Volume {
List documents = new ArrayList();
}
运行结果:
******序列化******
<volume>
<documents>d:\tmp</documents>
</volume>
******反序列化******
obj=Person对象的name=张三
obj=Person对象的name=毛毛
obj=Person对象的name=大熊
obj=1
obj=2.123
obj=Person对象的name=张三