XStream单例模式下反序列化Object-Mapping问题

本文出处:http://blog.csdn.net/chaijunkun/article/details/30257015,转载请注明。由于本人不定期会整理相关博文,会对相应内容作出完善。因此强烈建议在原始出处查看此文。

Java作为面向对象的语言,处理结构化的数据当然也可以将其对象化,这就是涉及到了转化工具。而对于XML文件来说,经常使用的是JDK 1.6开始支持的JAXB 2.0,另外还有一款叫做XStream的框架。本人在使用XStream遇到了一些问题,在此分享。


1.问题描述

在做项目的时候,为了提高性能,降低新建对象的频率,我开始时将XStream写成了单例模式。每当需要将对象序列化为xml,或者将xml反序列化为对象的时候,获取到这个XStream实例,然后调用转换API,但是在连续反序列化两个结构不同的XML的时候发现了抛出异常的bug。

2.基础代码

首先我写了一个测试用例,以下是XStream的单例模式和序列化对象和反序列化xml的代码实现:
public class TestCase {
	
	private static volatile XStream stream= null;
	
	public static XStream getStream(){
		synchronized (TestCase.class) {
			if (stream==null){
				stream= new XStream();
			}
			return stream;
		}
	}
	
	public static <T> void toXML(Object obj, Class<T> clazz, OutputStream out){
		XStream stream= TestCase.getStream();
		stream.processAnnotations(clazz);
		stream.toXML(obj, out);
		return;
	}
	
	public static <T> T fromXML(InputStream in, Class<T> clazz){
		XStream stream= TestCase.getStream();
		stream.processAnnotations(clazz);
		Object obj= stream.fromXML(in);
		try{
			return clazz.cast(obj);
		}catch(ClassCastException e){
			return null;
		}
	}
}

然后我构建了两个根节点相同,但子节点有差异的XML文件结构:

结构1:
<root date="2014-06-12 09:28:34.614 UTC">
	<demo>
		<element>test1</element>
	</demo>
	<demo>
		<element>test2</element>
	</demo>
</root>

结构2:
<root date="2014-06-12 09:28:34.745 UTC">
	<example>
		<element>test1</element>
	</example>
	<example>
		<element>test2</element>
	</example>
</root>


接下来对两个结构分别做了映射:

结构1:
package net.csdn.blog.chaijunkun.xml.case1;

import java.util.Date;
import java.util.List;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamImplicit;

@XStreamAlias("root")
public class Case1 {
	
	@XStreamAlias("date")
	@XStreamAsAttribute
	private Date date;
	
	@XStreamImplicit
	private List<Demo> demos;

	public Date getDate() {
		return date;
	}

	//一些getters and setters...	
}

package net.csdn.blog.chaijunkun.xml.case1;

import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("demo")
public class Demo {
	
	@XStreamAlias("element")
	private String element;

	//一些getters and setters...	
}

结构2:
package net.csdn.blog.chaijunkun.xml.case2;

import java.util.Date;
import java.util.List;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
import com.thoughtworks.xstream.annotations.XStreamImplicit;

@XStreamAlias("root")
public class Case2 {
	
	@XStreamAlias("date")
	@XStreamAsAttribute
	private Date date;
	
	@XStreamImplicit
	private List<Example> examples;

	//一些getters and setters...
}

package net.csdn.blog.chaijunkun.xml.case2;

import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("example")
public class Example {
	
	@XStreamAlias("element")
	private String element;

	//一些getters and setters...	
}

3.测试用例

为了进行后续的反序列化,我们先用对象序列化成xml文件,然后将文件再反序列化过来,看能否成功。

结构1的序列化:
public static void demo1() throws FileNotFoundException{
	Demo d1= new Demo();
	d1.setElement("test1");
	Demo d2= new Demo();
	d2.setElement("test2");
	List<Demo> demos= new LinkedList<Demo>();
	demos.add(d1);
	demos.add(d2);
	Case1 c1= new Case1();
	c1.setDemos(demos);
	c1.setDate(new Date());
	FileOutputStream out1= new FileOutputStream(new File("d:\\test1.xml"));
	try{
		TestCase.toXML(c1, Case1.class, out1);
	}finally{
		try {
			out1.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

结构2的序列化:
public static void demo2() throws FileNotFoundException{
	Example e1= new Example();
	e1.setElement("test1");
	Example e2= new Example();
	e2.setElement("test2");
	List<Example> examples= new LinkedList<Example>();
	examples.add(e1);
	examples.add(e2);
	Case2 c2= new Case2();
	c2.setExamples(examples);
	c2.setDate(new Date());
	FileOutputStream out2= new FileOutputStream(new File("d:\\test2.xml"));
	try{
		TestCase.toXML(c2, Case2.class, out2);
	}finally{
		try {
			out2.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

分别将两个结果保存为test1.xml和test2..xml之后,接下来尝试将它们反序列化过来:
public static void demo3() throws FileNotFoundException{
	File f1= new File("d:\\test1.xml");
	FileInputStream fis1= new FileInputStream(f1);
	Case1 c11= TestCase.fromXML(fis1, Case1.class);
	File f2= new File("d:\\test2.xml");
	FileInputStream fis2= new FileInputStream(f2);
	Case2 c22= TestCase.fromXML(fis2, Case2.class);
}

接下来执行JUnit测试用例:
@Test
public void doTest() throws FileNotFoundException{
	demo1();
	demo2();
	demo3();
}

发现抛出如下异常:
com.thoughtworks.xstream.converters.ConversionException: Element demo of type net.csdn.blog.chaijunkun.xml.case1.Demo is not defined as field in type net.csdn.blog.chaijunkun.xml.case2.Case2
---- Debugging information ----
class               : net.csdn.blog.chaijunkun.xml.case2.Case2
required-type       : net.csdn.blog.chaijunkun.xml.case2.Case2
converter-type      : com.thoughtworks.xstream.converters.reflection.ReflectionConverter
path                : /root/demo
line number         : 4
version             : null
-------------------------------
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.writeValueToImplicitCollection(AbstractReflectionConverter.java:403)
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:334)
	at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:234)
	at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:72)
	...........

在错误信息中,path指示要分析的二级节点是demo,而之前规定的结构2的二级节点是example,事实上test2.xml的二级节点也是example。这说明序列化输出没有问题。问题出在了反序列化上。根据错误信息,XStream只存储了第一次反序列化配置的结构,而第二次调用时并没有更新相关注解配置(即使调用了processAnnotations注解)。

4.解决方法

针对单例模式的失败,退而求其次,只能将其改为工厂模式:
public static XStream getStream(){
	return new XStream();
}

此时,两次反序列化都已正常。

由于一些扩展功能,自己的代码中实现了一个特殊的XStream Driver,注入这个Driver需要在XStream的构造过程中进行,因此将代码统一写进了getStream()方法中,以便每次得到的都是自定义的XStream(自定义Driver与本bug无关,去掉自定义Driver后bug依然可重现)。

5.写在后面

在静态方法toXML和fromXML中,我都加入了一个强制处理注解的方法调用processAnnotations。在官方API中,XStream实例有一个方法autodetectAnnotations可以设置自动处理注解。但是当反序列化时会出现更严重的类型无法转化问题。因此不建议使用该方法,而是强制使用processAnnotations。

XStream单例模式下反序列化Object-Mapping问题,,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。