puff_pig

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

问题描述:

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

  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。

posted on 2015-12-27 21:35  puff_pig  阅读(2666)  评论(0编辑  收藏  举报