本文出处: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.测试用例
结构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(); }
此时,两次反序列化都已正常。