解决方案(三)— 使用XStream解析Xml到JavaBean,保持属性默认值

使用场景

在Java开发中,有时我们需要自定义Xml,并使用常用的解析器XStream将Xml字符串InputStreamFile,解析成JavaBean对象。但是,有些时候,XML的标签属性,我们不需要额外赋值,它们通常只需要保持默认值。例如:

  • JavaBean类
@XStreamAlias("controllers")
public class ControllerGenerator {
	
	@XStreamAsAttribute
	private  String targetProject;
	
	@XStreamAsAttribute
	private  String targetFolder="src/main/java";
	
	@XStreamAsAttribute
	private  String targetPackage;
   
    /**
	 * ......
	 */
}
  • 对应XML
<controllers targetProject="kuaiban-platform"  targetPackage="kuaiban.platform.controller">
    /**
	 * ......
	 */
</controllers>

很显然,在XML中我们并没有直接定义属性 targetFolder 的值,而是希望它能够保持 Java类 中定义的初始值 “src/main/java”

然而,很遗憾,默认情况下,XStream并不支持这种操作。


转换器@XStreamConverter

    @XStreamAsAttribute
	@XStreamConverter(xxxConverter.class)
	private  String targetFolder="src/main/java";

首先想到的就是,实现 ConverterMatcher 接口,自定义一个转换器 xxxConverter

如果,是 BeanToXml@XStreamConverter将会很好的起到作用。
然而,很不幸,我们需要的是 XmlToBean,其工作原理,实际上就是解析XML中的字节码,而后进行处理(无非是顺序判断循环)。
既然,我们的XML内容中根本不包含 targetFolder这个字符串内容,那么程序自然也无法针对性的作做处理,属性targetFolder没有被处理,相应的,它的注解**@XStreamConverter(xxxConverter.class),也不可能被处理,所以在这里转换器 @XStreamConverter失效**。


原理

首先我们要来看一下,XStream最终构造函数

XStream xstream = new XStream(
        ReflectionProvider reflectionProvider, HierarchicalStreamDriver driver,
        ClassLoaderReference classLoaderReference, Mapper mapper, ConverterLookup converterLookup,
        ConverterRegistry converterRegistry);

ReflectionProvider 是参数之一,常见实现包含如下:

SunUnsafeReflectionProvider

  • XStream 缺省反射提供器,继承SunLimitedUnsafeReflectionProvider的**newInstance(Class)**调用 sun.misc.Unsafenative allocateInstance(Class) 本地方法为目标类分配实例属性值 **默认**为系统初始值 0falsenull

PureJavaReflectionProvider

  • 纯正的java反射提供器,实现接口ReflectionProvidernewInstance(Class)
    调用 java.lang.reflect.ConstructornewInstance(Class) java反射方法为目标类创建实例属性值自定义初始值用来保证反序列化的时候,使用 javaBean 中的默认值来表示 XML中没有显示指明的字段。
  • 但是其中的 writeField(Object,String,Object,Class)方法完全依照 reader 的值向实例中写入如果有特殊要求,请复写该方法。

解决方式

FieldDefaultValueProvider —— 自定义反射提供器

  • 继承 PureJavaReflectionProvider复写其中的 writeField(Object,String,Object,Class)方法 为reader去除首尾的space
  • 如果值为空白字符串取消注入,使用 javaBean中属性的默认值

实现代码:

public class FieldDefaultValueProvider extends PureJavaReflectionProvider {  
    /**
     * @param object 目标类的实例
     * @param fieldName XML中显示指明的字段
     * @param definedIn 父类或者类本身
     */
	@Override  
    public void writeField(Object object, String fieldName, Object value, Class definedIn) {
        Field field = fieldDictionary.field(object.getClass(), fieldName, definedIn);//返回存在于xml中的字段  
        validateFieldAccess(field);//验证字段可以被访问 
        try {  
            if (value instanceof String) {
				String trim = ((String)value).trim();//字符串首尾去空
				if(trim.length()==0)//如果是空字符串,则不做赋值,使用默认初始值
					return;
				field.set(object,trim);  
			}else{
				field.set(object, value);  
			}
        } catch (IllegalArgumentException e) {  
            throw new ObjectAccessException("Could not set field " + object.getClass() + "." + field.getName(), e);  
        } catch (IllegalAccessException e) {  
            throw new ObjectAccessException("Could not set field " + object.getClass() + "." + field.getName(), e);  
        }  
    }  
} 

使用方式:

public static <T> T  toBeanFromFile(InputStream in,Class<T> cls) throws Exception{
            XStream xstream=new XStream(new FieldDefaultValueProvider(),new Xpp3Driver());//默认支持DTD
            xstream.processAnnotations(cls);
            return (T)xstream.fromXML(in);        
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值