• android中Bean(Entity)间转换


    问题描述:

    之前的项目中,遇到一个问题,描述如下:

    1. 接口A请求结果转换后的EntityA与控件有高度耦合,控件大部分功能由EntityA的各属性来控制(这个看起来没有问题,虽然控件和业务逻辑最好能够分开,但是大部分情况下仍然很难做到);
    2. 有一个新需求,服务端提供了一个接口B,与接口A十分相似,并且希望能够复用原有的控件。

    在大部分情况下不会遇到这种情况,但是当有多个后端团队提供支持的时候,便有可能出现(譬如参数规范到底是驼峰式还是下划线?)。

    在java开发中,有很多Bean(Entity)间转换的工具,大部分采用了XML映射的方式。这种方式在服务端开发可能会更方便一些,但是在android开发中,对XML的属性文件支持不够(对XML属性文件的处理并不是很在行)。那么有没有对android更方便的Entity间转换方案呢?

    实现方案:


    基于java中注解和反射的使用,笔者编写如下一个工具类,可以实现android平台上entity间的转换,下面介绍这个工具。

    编写annotation与annotationHandler

    1. 首先,声明一个annotation。若EntityA要映射到EntityB的话,那么对EntityA中需要映射的属性,进行mapClass和mapProperty注解。

      对于单层Entity(Entity中属性均为基本类型或者String),只需要使用mapProperty即可。

      若为多层Entity(Entity中含有自定义的Entity属性),不仅需要使用mapProperty来设置对应的属性名,也需要mapClass来设置对应的类名了。

      代码如下所示:

    /**
     * 属性间的1:1映射关系
     *
     * Created by puff on 15/7/31.
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.FIELD)
    public @interface MapProperty {
     
        public String mapClass() default "";
     
        public String mapProperty() default "";
    }

    2. 然后,我们再写一个annotation-handler,如果不对annotation做处理的话,那就没什么意义了。

      工具类中第一个参数为源entity,第二个参数为目标entity类,换句话说我们把一个srcObj转到desObj,就需要有源obj和目标obj类两个参数。

      首先若是列表类型,则对列表做处理(json转换过来的entity很多都是有列表的)。因为列表(和数组类似)这种属性比较特殊,不是自定义Entity也不属于基本类型,我们需要做的就是遍历列表,把item挨个进行转换。
      其次,我们需要转换的无非就是一些基本类型和自定义类型了。备注写的比较详细,生成一个目标obj对象,通过反射检测源obj属性是否需要映射。不需要映射则直接pass(目标obj根本不需要这个属性)。若需要映射且是基本类型则直接取值并set进目标obj,若需要映射却是自定义类型则先递归处理再将结果set进去。

      代码如下:

     1 /**
     2  * Created by puff on 15/7/31.
     3  */
     4 public class AnnotationUtils {
     5     /**
     6      * *
     7      * 将源entity转为目标entity依赖于MapProperty
     8      *
     9      * @param obj 源entity
    10      * @param c   目标entity类
    11      * @return 目标entity
    12      * @throws Exception 反射处理annotation的异常
    13      */
    14     public static Object transformEntity(Object obj, Class c) throws Exception {
    15         if (obj == null || c == null) {
    16             return null;
    17         }
    18         if (obj instanceof List) {
    19             //列表单独处理
    20             List src = (List) obj;
    21             List result = new LinkedList();
    22             for (Object item : src) {
    23                 result.add(transformEntity(item, c));
    24             }
    25             return result;
    26         } else {
    27             //创建目标entity,待填充
    28             Object result = c.newInstance();
    29             Field[] declaredFields = obj.getClass().getDeclaredFields();
    30             for (Field field : declaredFields) {
    31                 //源属性开放属性权限
    32                 field.setAccessible(true);
    33                 if (field.isAnnotationPresent(MapProperty.class)) {
    34                     //获取源属性的映射注解
    35                     MapProperty annotation = field.getAnnotation(MapProperty.class);
    36                     Object value = field.get(obj);
    37                     if (!TextUtils.isEmpty(annotation.mapClass())) {
    38                         //复杂属性,递归处理
    39                         value = transformEntity(value, Class.forName(annotation.mapClass()));
    40                     }
    41                     Field resultField = c.getDeclaredField(annotation.mapProperty());
    42                     resultField.setAccessible(true);
    43                     resultField.set(result, value);
    44                 }
    45             }
    46             return result;
    47         }
    48     }
    49  
    50 }

    例子(将EntityA转为EntityB)

    1 /**
     2  *
     3  * @author puff
     4  */
     5 public class EntityA {
     6  
     7     @MapProperty(mapProperty = "value_1")
     8     private String value1;
     9     @MapProperty(mapProperty = "value_2", mapClass = "javaannotation.EntityB2")
    10     private List<List<EntityA2>> value2;
    11  
    12     public String getValue1() {
    13         return value1;
    14     }
    15  
    16     public void setValue1(String value1) {
    17         this.value1 = value1;
    18     }
    19  
    20     public List<List<EntityA2>> getValue2() {
    21         return value2;
    22     }
    23  
    24     public void setValue2(List<List<EntityA2>> value2) {
    25         this.value2 = value2;
    26     }
    27  
    28 }
    29  
    30 /**
    31  *
    32  * @author puff
    33  */
    34 public class EntityB {
    35  
    36     private String value_1;
    37     private List<List<EntityB2>> value_2;
    38  
    39     public String getValue_1() {
    40         return value_1;
    41     }
    42  
    43     public void setValue_1(String value_1) {
    44         this.value_1 = value_1;
    45     }
    46  
    47     public List<List<EntityB2>> getValue_2() {
    48         return value_2;
    49     }
    50  
    51     public void setValue_2(List<List<EntityB2>> value_2) {
    52         this.value_2 = value_2;
    53     }
    54  
    55 }
    56  
    57   public static void main(String[] args) throws Exception {
    58         EntityA entityA = getEntityA();
    59         Object object = transformEntity(entityA, EntityB.class);
    60         if (!(object instanceof EntityB)) {
    61             System.out.println("error1");
    62         }
    63         EntityB entityB = (EntityB) object;
    64         System.out.println("succ1");
    65     }

    局限性:
      因为后端的A,B接口基本上是一致的,如上的实现方法确实解决了笔者遇到的问题。

      但对于多层次Entity的情况来说,有可能出现层级不对称的问题:譬如要把一个boolean转化为自定义类的某个变量上,或当一个自定义类的某个变量转化为一个boolean的情况下,使用如上实现方法并不能解决问题。如果真的遇到了这种情况,并且该情况出现的比较少,则可以考虑单独取值后set。

  • 相关阅读:
    设计模式 ( 十七) 状态模式State(对象行为型)
    设计模式 ( 十六 ) 观察者模式Observer(对象行为型)
    设计模式 ( 十五 ) 中介者模式Mediator(对象行为型)
    设计模式 ( 十四 ) 迭代器模式Iterator(对象行为型)
    设计模式 ( 十三 ) 命令模式Command(对象行为型)
    设计模式(十一)代理模式Proxy(结构型)
    设计模式(十)享元模式Flyweight(结构型)
    开源项目
    C#中的Marshal
    给枚举加上Description,必要时,可以直接获取枚举类型代表的中文
  • 原文地址:https://www.cnblogs.com/puff/p/5080914.html
Copyright © 2020-2023  润新知