适时开始自己写工具吧,骚年——XML和对象的转化

在导入导出的时候,我们可以选用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

里面的值赋给一个对象,就是用的类似上面的方法。可当时还不知道反射可以这样做,所以也不觉得那样是重复。因

此,多看看项目源码,多学下人家的技巧是很有必要的。


总结:

好的代码不是一次就写出来的,一如前面我是在写了之后,才觉得重复,然后才去改的。适当的重构自己的代

码,你会学到更多~~~

相关文章

php输出xml格式字符串
J2ME Mobile 3D入门教程系列文章之一
XML轻松学习手册
XML入门的常见问题(一)
XML入门的常见问题(三)
XML轻松学习手册(2)XML概念