最近做微信支付开发,接口都是xml数据,决定应xstream。用之后发现spring的默认XStreamMarshaller不能达到要求,所以做了自定义
配置如下
这里有两个自己实现类CustomXStreamMarshaller和CustomXppDriver<bean id="xStreamMarshaller" class="cloudolp.CustomXStreamMarshaller"> <property name="annotatedClasses"> <array> <value>com.cloudolp.base.SmsResponse</value> <value>com.cloudolp.base.weixinpay.UnifiedorderRequest</value> <value>com.cloudolp.base.weixinpay.UnifiedorderResponse</value> </array> </property> <property name="streamDriver"> <bean class="cloudolp.CustomXppDriver"> <constructor-arg> <bean class="com.thoughtworks.xstream.io.naming.NoNameCoder"/> </constructor-arg> </bean> </property> </bean>
package cloudolp;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.*;
import com.thoughtworks.xstream.io.naming.NameCoder;
import com.thoughtworks.xstream.io.xml.*;
import java.io.Writer;
import java.lang.reflect.Field;
import org.springframework.oxm.xstream.XStreamMarshaller;
/**
* 增加ignoreUnknownElements
*/
public class CustomXStreamMarshaller extends XStreamMarshaller {
private boolean ignoreUnknow=false;
public CustomXStreamMarshaller() {
super();
}
public void setIgnoreUnknow(boolean ignoreUnknow) {
this.ignoreUnknow = ignoreUnknow;
}
@Override
protected void customizeXStream(XStream xstream) {
if(ignoreUnknow){
xstream.ignoreUnknownElements();
}
}
}
这个类直接继承spring默认实现的XStreamMarshaller ,增加了一个选项:反序列化时是否忽略对象中不存在的属性。微信返回的xml中的很多字段我是不需要的,如果没加入这个选项,自定义的bean中必须包含所有xml中的标签,否则反序列换出错。
package cloudolp;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.naming.NameCoder;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;
import java.io.Writer;
import java.lang.reflect.Field;
/**
* Created by chao.zeng on 2016/10/20.
*/
public class CustomXppDriver extends XppDriver {
public CustomXppDriver(){
super();
}
public CustomXppDriver(NameCoder nameCoder){
super(nameCoder);
}
@Override
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out,getNameCoder()) {
boolean cdata = false;
Class<?> targetClass = null;
private boolean needCDATA(Class<?> targetClass, String fieldAlias){
boolean cdata = false;
//first, scan self
cdata = existsCDATA(targetClass, fieldAlias);
if(cdata) return cdata;
//if cdata is false, scan supperClass until java.lang.Object
Class<?> superClass = targetClass.getSuperclass();
while(!superClass.equals(Object.class)){
cdata = existsCDATA(superClass, fieldAlias);
if(cdata) return cdata;
superClass = superClass.getClass().getSuperclass();
}
return false;
}
private boolean existsCDATA(Class<?> clazz, String fieldAlias){
//scan fields
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//1. exists XStreamCDATA
if(field.getAnnotation(XStreamCDATA.class) != null ){
XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);
//2. exists XStreamAlias
if(null != xStreamAlias){
if(fieldAlias.equals(xStreamAlias.value()))//matched
return true;
}else{// not exists XStreamAlias
if(fieldAlias.equals(field.getName()))
return true;
}
}
}
return false;
}
@Override
public void startNode(String name,@SuppressWarnings("rawtypes") Class clazz) {
super.startNode(name, clazz);
//业务处理,对于用XStreamCDATA标记的Field,需要加上CDATA标签
if(!name.equals("xml")){
cdata = needCDATA(targetClass, name);
}else{
targetClass = clazz;
}
}
@Override
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
if(text!=null && !text.startsWith("<![CDATA[")){
writer.write("<![CDATA["+text+"]]>");
}else{
writer.write("text");
}
} else {
writer.write(text);
}
}
};
}
}
package cloudolp;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by chao.zeng on 2016/10/20.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface XStreamCDATA {
}
有时候我们的标签的内容可能会包含特殊字符,这就需要使用<![CDATA[]]>将内容包裹起来,微信支付的很多标签都要求用<![CDATA[]]>包裹,这个自定义的driver就是实现此功能。
这里还有一个需要注意的类NoNameCoder,这个类不会把下划线转换成双下划线,而默认的是XmlFriendlyNameCoder会变成双下划线,微信支付的接口的很多XML都是a_b的结构,我们的bean属性的命名一一般是aB,所以会加上别名@XStreamAlias("a_b"),这时候一定要指定NoNameCoder,否则生成的标签是带双下划线的,导致接口调用失败