在导入导出的时候,我们可以选用excel、xml。
但当出现父子结构的样子时,用excel来处理未免就有点乏力了。这里就需要用更强大的xml来进行处理了。下面看下这个xml的结构吧。
<?xml version="1.0" encoding="UTF-8"?>
<!--
name:名称
sn:英文标识
priority:优先级
url:页面网址
description:描述
module:项目中的模块
permission:操作权限——1:查看,2:增加,3:删除,4:修改
-->
<system>
<module name="模块1" sn="mk1" priority="1" description="这是第一个模块" >
<permission id="1" name="查看"/>
<page name="页面1" sn="" priority="1" url="http://" description="这里第一个模块里面的页面">
<permission id="1" name="查看"/>
<permission id="2" name="增加"/>
</page>
</module>
<module name="模块2" sn="mk2" priority="2" description="这是第二个模块" >
<permission id="1" name="查看"/>
<page name="页面2" sn="qwer" priority="1" url="http://" description="这里第二个模块里面的页面">
<permission id="1" name="查看"/>
<permission id="2" name="增加"/>
</page>
</module>
</system>
那么对应要转成的对象是什么样子呢?
public class Tree<T>{
private String id;
private String name;
private T parent;
private List<T>children;
private boolean isleaf;
}
public class Module extends Tree<Module>{
private String url;
private String description;
private String sn;
private Integer priority = 99;
private List<Permission> permissions ;
private String organizationid;
}
现在要通过xml进行导入,xml中写的page和module都是Module类型的。
方法一:
通过dom4j,获取每个Element,然后对Attribute进行取,如(程序用到的代码较多,我只写一下核心部分):
Document dom=new SAXReader().read("读的xml文件");
List moduleXML=dom.selectNodes("/system/module");
List modules=new ArrayList();
for(Iteartor moduleIter=moduleXML.iteartor();moduleIter.hasNext();){
Element moduleEle=moduleIter.next();
Module module=new Module();
module.setName(moduleEle.attribute("name"));
module.setSn(moduleEle.attribute("sn"));
module.setPriority(Integer.parseInt(moduleEle.attribute("priority")));
……
List pageXML=moduleEle.elements("page");
List pages=new ArrayList();
for(Iteartor pageIter=pageXML.iteartor();pageIter.hasNext();){
Element pageEle=pageIter.next();
Module page=new Module();
page.setName(pageEle.attribute("name"));
page.setSn(pageEle.attribute("sn"));
page.setPriority(Integer.parseInt(pageEle.attribute("priority")));
……
pages.add(page);
}
module.setChildren(pages);
modules.add(module);
}
你是否看出了上面代码的坏味道呢?
1.代码重复
2.如果对xml想对page加一个属性,那么还需要改代码
现在我们需要做什么改变呢?跟着我来看看吧,还是反射的应用~
public class Dom2Object {
public static Object transfAtt2Obj(Class<?> clazz,Element element){
Object obj = null;
try {
obj=clazz.newInstance();
for (Iterator iterator = element.attributeIterator(); iterator.hasNext();) {
Attribute attribute = (Attribute) iterator.next();
setFieldValueInAllSuper(obj, attribute.getName(), attribute.getValue());
}
} catch (Exception e) {e.printStackTrace();}
return obj;
}
private static void setFieldValueInAllSuper(Object obj, String propertyName,Object value){
//获取当前Object的class
Class claszz=obj.getClass();
Field field = null;
do{
try{ //从类里面获取指定属性
field = claszz.getDeclaredField(propertyName);
}
catch(NoSuchFieldException e){//如果没有获取到,则设置为null
field=null;
}
//设置当前class为父class
claszz=claszz.getSuperclass();
} while(field==null&&claszz!=null); //当field为空且class不为空时,进行下次循环
//如果field为空,说明没有此字段,返回空
if(field==null) return;
//如果不为空,设置可见性,然后返回
field.setAccessible(true);
try {
//将要赋的值转到实体需要的类型,不然会报异常(例如String转Integer)
Object val;
//通过获取字段类型的构造函数来完成此操作(字段类型必须是包装类哦)
val = field.getType().getConstructor(String.class).newInstance(value);
field.set(obj, val);
} catch (Exception e) {
e.printStackTrace();
}
}
}
首先,是写一个为对象某属性赋值的方法,而属性是通过反射得到的。然后通过dom4j里的Attribute的API获取name和value,这样就可以动态的为对象的参数进行赋值了。
现在来看下进行方法提取后,之类的那些重复代码会变成什么样:
Document dom=new SAXReader().read("读的xml文件");
List moduleXML=dom.selectNodes("/system/module");
List modules=new ArrayList();
for(Iteartor moduleIter=moduleXML.iteartor();moduleIter.hasNext();){
Element moduleEle=moduleIter.next();
Module module=(Module)transfAtt2Obj(Module.class,moduleEle);
List pageXML=moduleEle.elements("page");
List pages=new ArrayList();
for(Iteartor pageIter=pageXML.iteartor();pageIter.hasNext();){
Element pageEle=pageIter.next();
Module page=(Module)transfAtt2Obj(Module.class,pageEle);
pages.add(page);
}
module.setChildren(pages);
modules.add(module);
}
应该很容易就看出来变化吧,那么这样写比前面那样写,优点在哪里呢?
1.复用性强,只要别人转的xml的时候,对象的属性在xml中都是以某标签的属性形式存在(可以扩展成标签也可以),那么这个方法就可以重用,因为这个抽取的方法和业务逻辑一点关系也没有。
2.扩展性好,一如前面说的,现在isleaf这个属性不必在xml中进行配置,但如果发现需要配置了,那么只需要在page这个标签的属性上加上isleaf="true"即可,代码可以完全不用动。
这么看是好处多多,那么又该注意什么呢?
1.用反射终究肯定会下降,不过当今时代的硬件,不用你考虑这个问题了
2.对于不常封装的我们来说,可能会碰到各种各样的问题,写这个方法用的时间,你写循环其实早写完了。不过对于现在的我们来说,还是可以接受的,通过项目学习、巩固基础嘛~~
3.自己写的工具类代码最好自己保留好,因为如果你看过我写的编程语言中的各种反射,你会发现
setFieldValueInAllSuper这个方法和那篇文章中的getFieldValueInAllSuper几乎一样,只不过一个是获取,一个是赋值。所以说代码经验的积累必不可少。
4.把握好度,够用就好。记住,你不是在写一个类库,够你用就好了,不然你的项目就可能延期了。一如我们用
的是将属性写在xml标签中的属性的位置上,而不是将属性以标签的形式展现,所以我就可以不考虑那种情况,如果
再去做两种兼容,那么可能一天也未必能弄出来。
5.嗅觉很重要,但见识更重要。一如曾经我们刚接触Java的时候,依然还记得将数据库中查出来的ResultSet
里面的值赋给一个对象,就是用的类似上面的方法。可当时还不知道反射可以这样做,所以也不觉得那样是重复。因
此,多看看项目源码,多学下人家的技巧是很有必要的。
总结:
好的代码不是一次就写出来的,一如前面我是在写了之后,才觉得重复,然后才去改的。适当的重构自己的代
码,你会学到更多~~~