首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网站开发 > XML SOAP >

XStream把xml文件转化作java对象(转)

2013-10-27 
XStream把xml文件转化为java对象(转)@XStreamAlias(input-file)publicstaticclass InputFileInfo {priva

XStream把xml文件转化为java对象(转)

@XStreamAlias("input-file")
publicstaticclass InputFileInfo {
private String type;
private String fileName;
XStream把xml文件转化作java对象(转)
}
publicstaticvoid main(String[] args) {
XmlRequest1 request = buildXmlRequest();
System.out.println(XmlRequest1.toXml(request));
}
privatestatic XmlRequest1 buildXmlRequest() {
XStream把xml文件转化作java对象(转)
}
}

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><?xml version="1.0" encoding="UTF-8"?>
<request from="levin@host" calculate-method="advanced">
<request-time>2012-11-28 17:11:54.664 UTC</request-time>
<input-files>
<input-file>
<type>DATA</type>
<fileName>data.2012.11.29.dat</fileName>
</input-file>
<input-file>
<type>CALENDAR</type>
<fileName>calendar.2012.11.29.dat</fileName>
</input-file>
</input-files>
</request>

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><?xml version="1.0" encoding="UTF-8"?>
<request from="levin@host" calculate-method="advanced">
<request-time>20121128T17:51:05</request-time>
<input-file type="DATA">data.2012.11.29.dat</input-file>
<input-file type="CALENDAR">calendar.2012.11.29.dat</input-file>
</request>

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->publicclass LevinDateConverter extends DateConverter {
public LevinDateConverter(String dateFormat) {
super(dateFormat, new String[] { dateFormat });
}
}

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->@XStreamConverter(value=LevinDateConverter.class, strings={"yyyyMMdd'T'HH:mm:ss"})
@XStreamAlias("request-time")
private Date requestTime;

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->@XStreamImplicit(itemFieldName="input-file")
private List<InputFileInfo> inputFiles;

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->@XStreamConverter(value=ToAttributedValueConverter.class, strings={"fileName"})
publicstaticclass InputFileInfo {
@XStreamAsAttribute
private String type;
private String fileName;
XStream把xml文件转化作java对象(转)
}

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->publicenum TimePeriod {
MONTHLY("monthly"), WEEKLY("weekly"), DAILY("daily");

private String name;

public String getName() {
return name;
}

private TimePeriod(String name) {
this.name = name;
}

publicstatic TimePeriod toEnum(String timePeriod) {
try {
return Enum.valueOf(TimePeriod.class, timePeriod);
} catch(Exception ex) {
for(TimePeriod period : TimePeriod.values()) {
if(period.getName().equalsIgnoreCase(timePeriod)) {
return period;
}
}
thrownew IllegalArgumentException("Cannot convert <"+ timePeriod +"> to TimePeriod enum");
}
}
}

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->publicclass LevinEnumSingleNameConverter extends EnumSingleValueConverter {
privatestaticfinal String CUSTOM_ENUM_NAME_METHOD ="getName";
privatestaticfinal String CUSTOM_ENUM_VALUE_OF_METHOD ="toEnum";

private Class<?extends Enum<?>> enumType;

public LevinEnumSingleNameConverter(Class<?extends Enum<?>> type) {
super(type);
this.enumType = type;
}

@Override
public String toString(Object obj) {
Method method = getCustomEnumNameMethod();
if(method ==null) {
returnsuper.toString(obj);
} else {
try {
return (String)method.invoke(obj, (Object[])null);
} catch(Exception ex) {
returnsuper.toString(obj);
}
}
}

@Override
public Object fromString(String str) {
Method method = getCustomEnumStaticValueOfMethod();
if(method ==null) {
return enhancedFromString(str);
}
try {
return method.invoke(null, str);
} catch(Exception ex) {
return enhancedFromString(str);
}
}

private Method getCustomEnumNameMethod() {
try {
return enumType.getMethod(CUSTOM_ENUM_NAME_METHOD, (Class<?>[])null);
} catch(Exception ex) {
returnnull;
}
}

private Method getCustomEnumStaticValueOfMethod() {
try {
Method method = enumType.getMethod(CUSTOM_ENUM_VALUE_OF_METHOD, (Class<?>[])null);
if(method.getModifiers() == Modifier.STATIC) {
return method;
}
returnnull;
} catch(Exception ex) {
returnnull;
}
}

private Object enhancedFromString(String str) {
try {
returnsuper.fromString(str);
} catch(Exception ex) {
for(Enum<?> item : enumType.getEnumConstants()) {
if(item.name().equalsIgnoreCase(str)) {
return item;
}
}
thrownew IllegalStateException("Cannot converter <"+ str +"> to enum <"+ enumType +">");
}
}
}

如下方式使用即可:

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->@XStreamAsAttribute
@XStreamAlias("time-period")
@XStreamConverter(value=LevinEnumSingleNameConverter.class)
private TimePeriod timePeriod;

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->publicclass FormatableDoubleConverter extends DoubleConverter {
private String pattern;
private DecimalFormat formatter;

public FormatableDoubleConverter(String pattern) {
this.pattern = pattern;
this.formatter =new DecimalFormat(pattern);
}

@Override
public String toString(Object obj) {
if(formatter ==null) {
returnsuper.toString(obj);
} else {
return formatter.format(obj);
}
}

@Override
public Object fromString(String str) {
try {
returnsuper.fromString(str);
} catch(Exception ex) {
if(formatter !=null) {
try {
return formatter.parse(str);
} catch(Exception e) {
thrownew IllegalArgumentException("Cannot parse <"+ str +"> to double value", e);
}
}
thrownew IllegalArgumentException("Cannot parse <"+ str +"> to double value", ex);
}
}

public String getPattern() {
return pattern;
}
}

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->@XStreamAsAttribute
@XStreamConverter(value=FormatableDoubleConverter.class, strings={"###,##0.0########"})
privatedouble value;

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

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><student name=”Levin”>
<city>shanghai</city>
<street>zhangjiang</street>
<code>201203</code>
</student>

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

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

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->publicclass Trade {
private String tradeId;
private Product product;
XStream把xml文件转化作java对象(转)
}
abstractclass Product {
private String name;
public Product(String name) {
this.name = name;
}
XStream把xml文件转化作java对象(转)
}
class FX extends Product {
privatedouble ratio;
public FX() {
super("fx");
}
XStream把xml文件转化作java对象(转)
}
class Future extends Product {
privatedouble maturity;
public Future() {
super("future");
}
XStream把xml文件转化作java对象(转)
}

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><trades>
<trade trade-id="001">
<product class="levin.xstream.blog.FX" name="fx" ratio="0.59"/>
</trade>
<trade trade-id="002">
<product class="levin.xstream.blog.Future" name="future" maturity="2.123"/>
</trade>
</trades>

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->xstream.registerConverter(new ProductConverter(
xstream.getMapper(), xstream.getReflectionProvider()));

public ProductConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
super(mapper, reflectionProvider);
}

@Override
publicboolean canConvert(@SuppressWarnings("rawtypes") Class type) {
return Product.class.isAssignableFrom(type);
}

@Override
protected Object instantiateNewInstance(HierarchicalStreamReader reader, UnmarshallingContext context) {
Object currentObject = context.currentObject();
if(currentObject !=null) {
return currentObject;
}

String name = reader.getAttribute("name");
if("fx".equals(name)) {
return reflectionProvider.newInstance(FX.class);
} elseif("future".equals(name)) {
return reflectionProvider.newInstance(Future.class);
}
thrownew IllegalStateException("Cannot convert <"+ name +"> product");
}
}

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><trades>
<trade trade-id="001">
<product name="fx" ratio="0.59"/>
</trade>
<trade trade-id="002">
<product name="future" maturity="2.123"/>
</trade>
</trades>

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

<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><trades>
<trade trade-id="001">
<fx ratio="0.59"/>
</trade>
<trade trade-id="002">
<future maturity="2.123"/>
</trade>
</trades>

大概找了一下,可能可以定义自己的Mapper来解决,不过XStream<!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />-->xstream.addDefaultImplementation(LinkedHashMap.class, Map.class);

热点排行