频道栏目
首页 > 资讯 > 其他 > 正文

通过别名映射将XML中的数据解析成Bean实例

16-10-27        来源:[db:作者]  
收藏   我要投稿

在最近开发过程中,需要解析XML获得相应的数据。XML元素的属性存放了数据信息,每一个XML元素中的属性代表一条数据。其格式如下所示:


    
    

对于以上格式的XML数据,每一个person元素对应于Java中的一个bean对象。通常,可以通过注解标注bean中的成员变量来使其和XML元素中的属性一一对应,这样就可以通过反射将XML中的数据解析成bean对象。但是使用注解的方式需要在创建Bean类的时候需要为成员变量添加额外的注解,在成员变量数目比较多的情况下,这也会增加不少的工作量。
在以往使用Hibernate的过程中,知道Hibernate有别名映射的功能。XML解析成Bean实例也可以通过别名映射的方式实现。博文通过剥离出Hibernate中数据解析成bean实例的部分,并添加定制的功能来实现XML中的别名映射。
在Hibernate的映射实现中,定义了PropertyAccessor接口,该接口将Java的反射操作以面向对象的方式进行封装,并提供Getter和Setter接口来表示Bean中的getFoo()和setFoo(Value)方法。其代码如下:

PropertyAccessor.java:

public abstract class PropertyAccessor {
    /**
     * 创建一个合适的Getter对象,表示Bean中的setFoo(value)方法
     *
     * @param clazz 类类型
     * @param propertyName 属性名
     * @return 返回一个合适的Getter对象,表示Bean中的setFoo(value)方法
     * @throws PropertyNotFoundException 异常:表示未找到属性
     */
    public abstract Getter getGetter(Class clazz, String propertyName) throws PropertyNotFoundException;

    /**
     * 创建一个合适的Setter对象,表示Bean中的setFoo(value)方法
     *
     * @param clazz 类类型
     * @param propertyName 属性名
     * @return 返回一个合适的Setter对象,表示Bean中属性对于的setFoo(value)方法
     * @throws PropertyNotFoundException 异常:表示未找到属性
     */
    public abstract Setter getSetter(Class clazz, String propertyName) throws PropertyNotFoundException;
}

Getter.java:

public abstract class Getter {
    /**
     * 从Obj实例中获得属性值
     * @param obj 实例对象
     * @return 返回属性值
     * @throws GetterNotFoundException
     */
    public abstract Object get(Object obj) throws GetterNotFoundException;

    /**
     * 获得返回类型
     *
     * @return 返回类型
     */
    public abstract Class getReturnType();
}

Setter.java

public abstract class Setter {
    /**
     * 设置属性到实例中
     *
     * @param obj 实例.
     * @param value 值.
     * @throws SetterNotFoundException
     */
    public abstract void set(Object obj, Object value) throws SetterNotFoundException;

    /**
     * 获得值类型
     */
    public abstract Class getParamType();
}

对于PropertyAccessor、Setter、Getter接口的实现,大家可以查看Hibernate的源码深入了解。此处就不再详细展示,但在文末会提供定制过的实现类。
在Hibernate映射实现中,还定义了Transformer接口,用来实现数据转化成Bean对象。由于项目开发过程中,XML中的属性名有可能发生变化,但默认都是以下划线命名法,因此定制增加一个属性名转化接口,来将XML中的属性名映射成Bean中的属性名。此外,由于XML中的属性值都是字符串类型的,而Bean中的值类型并不固定,因此还定制实现一个值类型转化接口。对于属性名转化和值类型转化接口都有默认实现,也可支持自定义实现。
Transformer:

public abstract class Transformer {
    /**
     * tuples表示属性值数组,aliases表示属性名数组,transform方法则将属性值和属性名
     * 转化成对于的Bean实例
     *
     * @param tuples 属性值数组
     * @param aliases 属性名数组
     * @return Bean实例
     */
    public abstract Object transform(Object[] tuples, String[] aliases);
}

AliasConverter.class:

    /**
     * 别名转化器
     * 
     * @author Administrator
     *
     */
    public static abstract class AliasConverter {
        public abstract String convert2SuitName(String aliasName);
    }

    /**
     * 默认别名转化器
     * 
     * @author Administrator
     *
     */
    public static class DefaultAliasConvert extends AliasConverter {

        /**
         * 将下划线法命名改为驼峰命名
         */
        @Override
        public String convert2SuitName(String aliasName) {
            return aliasName.replaceAll("_", "");
        }
    }

TupleConverter:

    /**
     * 值类型转化器:
     * 在赋值过程中,可能存在tuple类型和setter方法值类型不一致的情况。
     * 通过值类型转换器将tuple值转化为合适的类型。
     * 
     * @author Administrator
     *
     */
    public static abstract class TupleConverter {
        protected Logger logger = LoggerFactory.getLogger(TupleConverter.class);

        public abstract Object convert2SuitTuple(Object obj, Class suitType);
    }

    protected static class DefaultTupleConvert extends TupleConverter {

        @Override
        public Object convert2SuitTuple(Object obj, Class suitType) {
            if (obj == null || suitType == null) {
                return obj;
            }

            // 如果obj的类型是suitType的子类,则直接转换
            // 如果obj不是字符串类型,则不进行转换到目标类型,直接返回            
            if (suitType.isAssignableFrom(obj.getClass()) 
                    || !(obj instanceof String)) {
                return obj;
            }

            String suitTypeName = suitType.getName();
            // try setFoo(int)
            if ("java.lang.Integer".equals(suitTypeName)
                    || "int".equals(suitTypeName)) {
                return Integer.valueOf((String) obj);
            }
            // try setFoo(long)
            else if ("java.lang.Long".equals(suitTypeName)
                        || "long".equals(suitTypeName)) {
                return Long.valueOf((String) obj);
            }
            // try setFoo(boolean)
            else if ("java.long.Boolean".equals(suitTypeName)
                        || "boolean".equals(suitTypeName)) {
                return Boolean.valueOf((String) obj);
            }
            // try setFoo(Date)
            else if ("java.util.Date".equals(suitTypeName)
                        || "java.sql.Date".equals(suitTypeName)) {

            }
            // try setFoo(Calendar)
            else if ("java.util.Calendar".equals(suitTypeName)) {

            }

            return obj;
        }

    }

最后,通过继承实现Transformer来完成别名映射的过程。其实现如下:

public class AliasToBeanResultTransformer extends Transformer {

    private final Class resultClass;
    private final AliasConverter aliasConverter;
    private final TupleConverter tupleConverter;
    private final PropertyAccessor propertyAccessor;
    private String[] aliases;       // cache for aliases
    private Setter[] setters;       // cache for setters
    private boolean isInitialilzed;

    public AliasToBeanResultTransformer(Class resultClass, TupleConverter tupleConverter, AliasConverter aliasConverter) {
        if (resultClass == null) {
            throw new IllegalArgumentException( "resultClass can not be null");
        }
        // 目标对象
        this.resultClass = resultClass;

        // 值类型转换器
        this.tupleConverter = tupleConverter == null ? new DefaultTupleConvert() : tupleConverter;
        // 别名转换器
        this.aliasConverter = aliasConverter == null ? new DefaultAliasConvert() : aliasConverter;

        propertyAccessor = new ChainedPropertyAccessor(
                new PropertyAccessor[] {
                        PropertyAccessorFactory.getPropertyAccessor("basic"),
                        PropertyAccessorFactory.getPropertyAccessor("field")
                }
        );

        isInitialilzed = false;
    }

    /**
     * 
     * 获得Setter集合
     * 
     * @param aliases
     */
    private void initialize(String[] aliases) {
        this.aliases = aliases;
        setters = new Setter[aliases.length];
        for ( int i = 0; i < aliases.length; i++ ) {
            String alias = aliases[i];
            if (alias != null) {
                // 获得合适的Setter
                setters[ i ] = propertyAccessor.getSetter(resultClass, 
                        // 把别名转化为合适的名称
                        aliasConverter.convert2SuitName(alias));
            }
        }
        isInitialilzed = true;
    }

    @Override
    public Object transform(Object[] tuples, String[] aliases) {
        Object result;

        try {
            if (!isInitialilzed) {
                initialize(aliases);
            } else {
                // 校验aliases是否发生改变
                check(aliases);
            }

            result = resultClass.newInstance();

            for ( int i = 0; i < aliases.length; i++ ) {
                if ( setters[i] != null ) {
                    // 赋值
                    setters[i].set( result,
                            // 把值转化为合适的类型
                            tupleConverter.convert2SuitTuple(tuples[i], setters[i].getParamType()));
                }
            }

        } catch (InstantiationException e) {
            throw new CmsException("Could not instantiate resultclass: " + resultClass.getName());
        } catch (IllegalAccessException e) {
            throw new CmsException("Could not instantiate resultclass: " + resultClass.getName());
        }

        return result;
    }

    private void check(String[] aliases) {
        if ( ! Arrays.equals( aliases, this.aliases ) ) {
            throw new IllegalStateException(
                    "aliases are different from what is cached; aliases=" + Arrays.asList( aliases ) +
                            " cached=" + Arrays.asList( this.aliases ) );
        }
    }
}

至此,实现了别名映射的过程。对于如何将XML中的数据映射成Bean实例,关键是定义别名映射的规则并自定义实现别名转化器。其使用方式如下:

    @Test
    public void testTransform() {
        // ...省略解析XML的过程,一般封装为工具类
        // 假设解析XML后获得的属性值
        Object[] tuples = {"1", "hello world"};
        // 假设解析XML后获得的属性名
        String[] aliases = {"i_Num", "str_Value"};
        // XML中的属性名采用下划线命名法,Bean中的属性名采用驼峰命名法,
        // 程序默认的别名转化器是下划线命名法转化为驼峰命名法,故别名转化器传入Null以使用默认的
        AliasToBeanResultTransformer transformer = 
                    new AliasToBeanResultTransformer(TestBo.class, null, null);
        // 获得Bo类
        TestBo testBo = (TestBo) transformer.transform(tuples, aliases);
        assertEquals(1, testBo.getiNum());
        assertEquals("hello world", testBo.getStrValue());
    }

    // 定义测试的Bo类
    public static class TestBo {
        private int iNum;
        private String strValue;
        public int getiNum() {
            return iNum;
        }
        public void setiNum(int iNum) {
            this.iNum = iNum;
        }
        public String getStrValue() {
            return strValue;
        }
        public void setStrValue(String strValue) {
            this.strValue = strValue;
        }
    }
相关TAG标签
上一篇:WCF服务通过TCP实时监控客户端状态,并可以向客户端广播推送消息,实现双向通信
下一篇:Java设计模式:建造者模式
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站