在匯入匯出的時候,我們可以選用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的classClass claszz=obj.getClass(); Field field = null; do{ try{ //從類裡面擷取指定屬性field = claszz.getDeclaredField(propertyName); } catch(NoSuchFieldException e){//如果沒有擷取到,則設定為nullfield=null; }//設定當前class為父classclaszz=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
裡面的值賦給一個對象,就是用的類似上面的方法。可當時還不知道反射可以這樣做,所以也不覺得那樣是重複。因
此,多看看項目源碼,多學下人家的技巧是很有必要的。
總結:
好的代碼不是一次就寫出來的,一如前面我是在寫了之後,才覺得重複,然後才去改的。適當的重構自己的代
碼,你會學到更多~~~