使用XStream序列化、反序列化XML数据时遇到的各种问题

前端之家收集整理的这篇文章主要介绍了使用XStream序列化、反序列化XML数据时遇到的各种问题前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

转载:http://www.blogjava.net/DLevin/archive/2012/11/30/392240.html@H_301_2@

首先对于简单的引用,@H_301_2@XStream使用起来确实比较简单,比如自定义标签属性、使用属性和使用子标签的定义等:
@H_301_2@

@XStreamAlias(@H_301_2@ "@H_301_2@ request@H_301_2@ )
@H_301_2@ public@H_301_2@ @H_301_2@ class@H_301_2@ XmlRequest1{
@H_301_2@ private@H_301_2@ static@H_301_2@ XStreamxstream;
@H_301_2@ {
xstream@H_301_2@ =@H_301_2@ new@H_301_2@ XStream();
xstream.autodetectAnnotations(@H_301_2@ true@H_301_2@ );
}

@XStreamAsAttribute
@H_301_2@ Stringfrom;

@XStreamAsAttribute
@XStreamAlias(@H_301_2@ calculate-method@H_301_2@ StringcalculateMethod;

@XStreamAlias(@H_301_2@ request-time@H_301_2@ private@H_301_2@ DaterequestTime;

@XStreamAlias(@H_301_2@ input-files@H_301_2@ List@H_301_2@ <@H_301_2@ InputFileInfo@H_301_2@ >@H_301_2@ inputFiles;

@H_301_2@ StringtoXml(XmlRequest1request){
StringWriterwriter@H_301_2@ StringWriter();
writer.append(Constants.XML_HEADER);
xstream.toXML(request,writer);
@H_301_2@ return@H_301_2@ writer.toString();
}
@H_301_2@ XmlRequest1toInstance(StringxmlContent){
@H_301_2@ (XmlRequest1)xstream.fromXML(xmlContent);
}

@XStreamAlias(@H_301_2@ input-file@H_301_2@ InputFileInfo{
@H_301_2@ Stringtype;
@H_301_2@ StringfileName;

}
@H_301_2@ void@H_301_2@ main(String[]args){
XmlRequest1request@H_301_2@ buildXmlRequest();
System.out.println(XmlRequest1.toXml(request));
}
@H_301_2@ XmlRequest1buildXmlRequest(){

}
}@H_301_2@

对以上@H_301_2@Request定义,我们可以得到如下结果:@H_301_2@

<?@H_301_2@ xmlversion="1.0"encoding="UTF-8"@H_301_2@ ?>@H_301_2@
@H_301_2@ <@H_301_2@ request@H_301_2@ from@H_301_2@ ="levin@host"@H_301_2@ ="advanced"@H_301_2@ >@H_301_2@ 2012-11-2817:11:54.664UTC@H_301_2@ </@H_301_2@ type@H_301_2@ DATA@H_301_2@ fileName@H_301_2@ data.2012.11.29.dat@H_301_2@ CALENDAR@H_301_2@ calendar.2012.11.29.dat@H_301_2@ >@H_301_2@

可惜这个世界不会那么清净,这个格式有些时候貌似并不符合要求,比如@H_301_2@request-time的格式、@H_301_2@input-files的格式,我们实际需要的格式是这样的:
@H_301_2@

20121128T17:51:05@H_301_2@ input-file@H_301_2@ ="DATA"@H_301_2@ ="CALENDAR"@H_301_2@ 对不同@H_301_2@Date格式的支持可以是用@H_301_2@Converter实现,在@H_301_2@XStream中默认使用自己实现的@H_301_2@DateConverter,它支持的格式是:@H_301_2@yyyy-MM-dd HH:mm:ss.S 'UTC'@H_301_2@,然而我们现在需要的格式是@H_301_2@yyyy-MM-dd’T’HH:mm:ss@H_301_2@,如果使用@H_301_2@XStream直接注册@H_301_2@DateConverter,可以使用配置自己的@H_301_2@DateConverter,但是由于@H_301_2@DateConverter的构造函数的定义以及@H_301_2@@XStreamConverter的构造函数参数的支持方式的限制,貌似@H_301_2@DateConverter不能很好的支持注解方式的注册,因而我时间了一个自己的@H_301_2@DateConverter支持注解:
@H_301_2@

LevinDateConverter@H_301_2@ extends@H_301_2@ DateConverter{
@H_301_2@ LevinDateConverter(StringdateFormat){
@H_301_2@ super@H_301_2@ (dateFormat,@H_301_2@ String[]{dateFormat});
}
}@H_301_2@

在@H_301_2@requestTime字段中需要加入以下注解定义:
@H_301_2@

@XStreamConverter(value@H_301_2@ LevinDateConverter.@H_301_2@ ,strings@H_301_2@ {@H_301_2@ yyyyMMdd'T'HH:mm:ss@H_301_2@ })
@XStreamAlias(@H_301_2@ DaterequestTime;@H_301_2@

对集合类,@H_301_2@XStream提供了@H_301_2@@XStreamImplicit注解,以将集合中的内容摊平到上一层@H_301_2@XML元素中,其中@H_301_2@itemFieldName的值为其使用的标签名,此时@H_301_2@InputFileInfo类中不需要@H_301_2@@XStreamAlias标签的定义:
@H_301_2@

@XStreamImplicit(itemFieldName@H_301_2@ inputFiles;@H_301_2@

对@H_301_2@InputFileInfo中的字段,@H_301_2@type作为属性很容易,只要为它加上@H_301_2@@XStreamAsAttribute注解即可,而将@H_301_2@fileName作为@H_301_2@input-file标签的一个内容字符串,则需要使用@H_301_2@ToAttributedValueConverter,其中@H_301_2@Converter的参数为需要作为字符串内容的字段名:
@H_301_2@

ToAttributedValueConverter.@H_301_2@ })
@H_301_2@ InputFileInfo{
@XStreamAsAttribute
@H_301_2@ StringfileName;

}@H_301_2@

XStream对枚举类型的支持貌似不怎么好,默认注册的@H_301_2@EnumSingleValueConverter只是使用了@H_301_2@Enum提供的@H_301_2@name()和静态的@H_301_2@valueOf()方法将@H_301_2@enum转换成@H_301_2@String或将@H_301_2@String转换回@H_301_2@enum。然而有些时候@H_301_2@XML的字符串和类定义的@H_301_2@enum值并不完全匹配,最常见的就是大小写的不匹配,此时需要写自己的@H_301_2@Converter。在这种情况下,我一般会在@H_301_2@enum中定义一个@H_301_2@name属性,这样就可以自定义@H_301_2@enum的字符串表示。比如有@H_301_2@TimePeriod的@H_301_2@enum
@H_301_2@

enum@H_301_2@ TimePeriod{
MONTHLY(@H_301_2@ monthly@H_301_2@ ),WEEKLY(@H_301_2@ weekly@H_301_2@ 301_2@ daily@H_301_2@ );

@H_301_2@ Stringname;

@H_301_2@ StringgetName(){
@H_301_2@ name;
}

@H_301_2@ TimePeriod(Stringname){
@H_301_2@ this@H_301_2@ .name@H_301_2@ TimePeriodtoEnum(StringtimePeriod){
@H_301_2@ try@H_301_2@ {
@H_301_2@ Enum.valueOf(TimePeriod.@H_301_2@ 301_2@ catch@H_301_2@ (Exceptionex){
@H_301_2@ for@H_301_2@ (TimePeriodperiod:TimePeriod.values()){
@H_301_2@ if@H_301_2@ (period.getName().equalsIgnoreCase(timePeriod)){
@H_301_2@ period;
}
}
@H_301_2@ throw@H_301_2@ IllegalArgumentException(@H_301_2@ Cannotconvert<@H_301_2@ +@H_301_2@ timePeriod@H_301_2@ >toTimePeriodenum@H_301_2@ );
}
}
}@H_301_2@

我们可以编写以下@H_301_2@Converter以实现对枚举类型的更宽的容错性:
@H_301_2@

LevinEnumSingleNameConverter@H_301_2@ EnumSingleValueConverter{
@H_301_2@ final@H_301_2@ StringCUSTOM_ENUM_NAME_METHOD@H_301_2@ getName@H_301_2@ ;
@H_301_2@ StringCUSTOM_ENUM_VALUE_OF_METHOD@H_301_2@ toEnum@H_301_2@ ;

@H_301_2@ Class@H_301_2@ <?@H_301_2@ Enum@H_301_2@ <?>>@H_301_2@ enumType;

@H_301_2@ LevinEnumSingleNameConverter(Class@H_301_2@ type){
@H_301_2@ (type);
@H_301_2@ .enumType@H_301_2@ type;
}

@Override
@H_301_2@ StringtoString(Objectobj){
Methodmethod@H_301_2@ getCustomEnumNameMethod();
@H_301_2@ (method@H_301_2@ ==@H_301_2@ null@H_301_2@ ){
@H_301_2@ .toString(obj);
}@H_301_2@ else@H_301_2@ (String)method.invoke(obj,(Object[])@H_301_2@ );
}@H_301_2@ .toString(obj);
}
}
}

@Override
@H_301_2@ ObjectfromString(Stringstr){
Methodmethod@H_301_2@ getCustomEnumStaticValueOfMethod();
@H_301_2@ enhancedFromString(str);
}
@H_301_2@ method.invoke(@H_301_2@ 301_2@ enhancedFromString(str);
}
}

@H_301_2@ MethodgetCustomEnumNameMethod(){
@H_301_2@ enumType.getMethod(CUSTOM_ENUM_NAME_METHOD,(Class@H_301_2@ <?>@H_301_2@ [])@H_301_2@ ;
}
}

@H_301_2@ MethodgetCustomEnumStaticValueOfMethod(){
@H_301_2@ {
Methodmethod@H_301_2@ enumType.getMethod(CUSTOM_ENUM_VALUE_OF_METHOD,0)">);
@H_301_2@ (method.getModifiers()@H_301_2@ Modifier.STATIC){
@H_301_2@ method;
}
@H_301_2@ ;
}@H_301_2@ ObjectenhancedFromString(Stringstr){
@H_301_2@ .fromString(str);
}@H_301_2@ (Enum@H_301_2@ item:enumType.getEnumConstants()){
@H_301_2@ (item.name().equalsIgnoreCase(str)){
@H_301_2@ item;
}
}
@H_301_2@ IllegalStateException(@H_301_2@ Cannotconverter<@H_301_2@ str@H_301_2@ >toenum<@H_301_2@ enumType@H_301_2@ 如下方式使用即可:
@H_301_2@

@XStreamAsAttribute
@XStreamAlias(@H_301_2@ time-period@H_301_2@ )
@XStreamConverter(value@H_301_2@ LevinEnumSingleNameConverter.@H_301_2@ TimePeriodtimePeriod;@H_301_2@

对@H_301_2@double类型,貌似默认的@H_301_2@DoubleConverter实现依然不给力,它不支持自定义的格式,比如我们想在序列化的时候用一下格式:@H_301_2@”###,##0.0########@H_301_2@”,此时又需要编写自己的@H_301_2@Converter
@H_301_2@

FormatableDoubleConverter@H_301_2@ DoubleConverter{
@H_301_2@ Stringpattern;
@H_301_2@ DecimalFormatformatter;

@H_301_2@ FormatableDoubleConverter(Stringpattern){
@H_301_2@ .pattern@H_301_2@ pattern;
@H_301_2@ .formatter@H_301_2@ DecimalFormat(pattern);
}

@Override
@H_301_2@ StringtoString(Objectobj){
@H_301_2@ (formatter@H_301_2@ formatter.format(obj);
}
}

@Override
@H_301_2@ ObjectfromString(Stringstr){
@H_301_2@ !=@H_301_2@ formatter.parse(str);
}@H_301_2@ (Exceptione){
@H_301_2@ Cannotparse<@H_301_2@ >todoublevalue@H_301_2@ 301_2@ 301_2@ StringgetPattern(){
@H_301_2@ pattern;
}
}@H_301_2@

使用方式和之前的@H_301_2@Converter类似:
@H_301_2@

@XStreamAsAttribute
@XStreamConverter(value@H_301_2@ FormatableDoubleConverter.@H_301_2@ ###,##0.0########@H_301_2@ double@H_301_2@ value;@H_301_2@

最后,还有两个@H_301_2@XStream没法实现的,或者说我没有找到一个更好的实现方式的场景。第一种场景是@H_301_2@XStream不能很好的处理对象组合问题:@H_301_2@

在面向对象编程中,一般尽量的倾向于抽取相同的数据成一个类,而通过组合的方式构建整个数据结构。比如@H_301_2@Student类中有@H_301_2@name、@H_301_2@address,@H_301_2@Address是一个类,它包含@H_301_2@city、@H_301_2@code、@H_301_2@street等信息,此时如果要对@H_301_2@Student对象做如下格式序列化:
@H_301_2@

student@H_301_2@ name@H_301_2@ =”Levin”>
@H_301_2@ <city@H_301_2@ shanghai@H_301_2@ city@H_301_2@ street@H_301_2@ zhangjiang@H_301_2@ code@H_301_2@ 201203@H_301_2@ student@H_301_2@ 貌似我没有找到可以实现的方式,@H_301_2@XStream能做是在中间加一层@H_301_2@address标签。对这种场景的解决方案,一种是将@H_301_2@Address中的属性平摊到@H_301_2@Student类中,另一种是让@H_301_2@Student继承自@H_301_2@Address类。不过貌似这两种都不是比较理想的办法。@H_301_2@

第二种场景是@H_301_2@XStream不能很好的处理多态问题:@H_301_2@

比如我们有一个@H_301_2@Trade类,它可能表示不同的产品:
@H_301_2@

Trade{
@H_301_2@ StringtradeId;
@H_301_2@ Productproduct;

}
@H_301_2@ abstract@H_301_2@ Product{
@H_301_2@ Stringname;
@H_301_2@ Product(Stringname){
@H_301_2@ name;
}

}
@H_301_2@ FX@H_301_2@ ratio;
@H_301_2@ FX(){
@H_301_2@ (@H_301_2@ fx@H_301_2@ );
}

}
@H_301_2@ Future@H_301_2@ maturity;
@H_301_2@ Future(){
@H_301_2@ future@H_301_2@ );
}

}@H_301_2@

通过一些简单的设置,我们能得到如下@H_301_2@XML格式:
@H_301_2@

trades@H_301_2@ trade@H_301_2@ trade-id@H_301_2@ ="001"@H_301_2@ product@H_301_2@ class@H_301_2@ ="levin.xstream.blog.FX"@H_301_2@ ="fx"@H_301_2@ ratio@H_301_2@ ="0.59"@H_301_2@ />@H_301_2@ trade@H_301_2@ ="002"@H_301_2@ ="levin.xstream.blog.Future"@H_301_2@ ="future"@H_301_2@ maturity@H_301_2@ ="2.123"@H_301_2@ >@H_301_2@

作为数据文件,对@H_301_2@Java类的定义显然是不合理的,因而简单一些,我们可以编写自己的@H_301_2@Converter将@H_301_2@class属性从@H_301_2@product去除
@H_301_2@

xstream.registerConverter(@H_301_2@ ProductConverter(
xstream.getMapper(),xstream.getReflectionProvider()));

@H_301_2@ ProductConverter(Mappermapper,ReflectionProviderreflectionProvider){
@H_301_2@ (mapper,reflectionProvider);
}

@Override
@H_301_2@ boolean@H_301_2@ canConvert(@SuppressWarnings(@H_301_2@ rawtypes@H_301_2@ )Classtype){
@H_301_2@ Product.@H_301_2@ .isAssignableFrom(type);
}

@Override
@H_301_2@ protected@H_301_2@ ObjectinstantiateNewInstance(HierarchicalStreamReaderreader,UnmarshallingContextcontext){
ObjectcurrentObject@H_301_2@ context.currentObject();
@H_301_2@ (currentObject@H_301_2@ currentObject;
}

Stringname@H_301_2@ reader.getAttribute(@H_301_2@ .equals(name)){
@H_301_2@ reflectionProvider.newInstance(FX.@H_301_2@ reflectionProvider.newInstance(Future.@H_301_2@ );
}
@H_301_2@ >product@H_301_2@ );
}
}@H_301_2@

在所有@H_301_2@Production上定义@H_301_2@@XStreamAlias(“product”)注解。这时的@H_301_2@XML输出结果为:
@H_301_2@

然而如果有人希望@H_301_2@XML输出结果如下呢@H_301_2@?

fx@H_301_2@ future@H_301_2@ 大概找了一下,可能可以定义自己的@H_301_2@Mapper解决,不过@H_301_2@XStream的源码貌似比较复杂,没有时间深究这个问题,留着以后慢慢解决吧。@H_301_2@

补充:

对Map类型数据,XStream默认使用以下格式显示

<@H_301_2@ map@H_301_2@ class@H_301_2@ ="linked-hash-map"@H_301_2@ >@H_301_2@
@H_301_2@ entry@H_301_2@ string@H_301_2@ key1@H_301_2@ </@H_301_2@ value1@H_301_2@ key2@H_301_2@ value2@H_301_2@ map@H_301_2@ >@H_301_2@

但是对一些简单的Map,我们希望如下显示

@H_301_2@ entry@H_301_2@ key@H_301_2@ ="key1"@H_301_2@ value@H_301_2@ ="value1"@H_301_2@ />@H_301_2@ ="key2"@H_301_2@ ="value2"@H_301_2@ >@H_301_2@

对这种需求需要通过编写Converter解决,继承自MapConverter,覆盖以下函数,这里的Map默认key和value都是String类型,如果他们不是String类型,需要另外添加逻辑:

@SuppressWarnings(@H_301_2@ "@H_301_2@ rawtypes@H_301_2@ )
@Override
@H_301_2@ public@H_301_2@ void@H_301_2@ marshal(Objectsource,HierarchicalStreamWriterwriter,
MarshallingContextcontext){
Mapmap@H_301_2@ =@H_301_2@ (Map)source;
@H_301_2@ for@H_301_2@ (Iteratoriterator@H_301_2@ map.entrySet().iterator();iterator.hasNext();){
Entryentry@H_301_2@ (Entry)iterator.next();
ExtendedHierarchicalStreamWriterHelper.startNode(writer,mapper()
.serializedClass(Map.Entry.@H_301_2@ class@H_301_2@ 301_2@ 301_2@ 301_2@ unchecked@H_301_2@ 301_2@ })
@H_301_2@ protected@H_301_2@ putCurrentEntryIntoMap(HierarchicalStreamReaderreader,
UnmarshallingContextcontext,Mapmap,Maptarget){
Objectkey@H_301_2@ reader.getAttribute(@H_301_2@ );
Objectvalue@H_301_2@ );

target.put(key,value);
}@H_301_2@

但是只是使用Converter,得到的结果多了一个class属性

>@H_301_2@

在XStream中,如果定义的字段是一个父类或接口,在序列化是会默认加入class属性以确定反序列化时用的类,为了去掉这个class属性,可以定义默认的实现类来解决(虽然感觉这种解决方案不太好,但是目前还没有找到更好的解决方案)。

@H_301_2@

xstream.addDefaultImplementation(LinkedHashMap.@H_301_2@class@H_301_2@301_2@);@H_301_2@@H_301_2@
原文链接:/xml/300331.html

猜你在找的XML相关文章