xstream与resttemplate的联合使用

最近做微信支付开发,接口都是xml数据,决定应xstream。用之后发现spring的默认XStreamMarshaller不能达到要求,所以做了自定义

配置如下

<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>


这里有两个自己实现类CustomXStreamMarshaller和CustomXppDriver

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,否则生成的标签是带双下划线的,导致接口调用失败

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值