Digester解析xml文件
一般用來讀取xml文件的工具包有DOM、SAX和JDOM等,但用過的人都知道,它們屬于比較底層的API,寫起來代碼量很大,而且如果修改了xml文件的格式,代碼也要做大幅度的改動。而使用Apache Jakarta的Digester,解析XML文件非常方便且不需要過多的關心底層的具體解析過程。Digester本來僅僅是Jakarta Struts中的一個工具,用于處理struts-config.xml配置文件。顯然,將XML文件轉換成相應的Java對象是一項很通用的功能,這個工具理應具有更廣泛的用途,所以很快它就在Jakarta Commons項目(用于提供可重用的Java組件庫)中有了一席之地。Digester由"事件"驅動,通過調用預定義的規則操作對象棧,將XML文件轉換為Java對象。
工作原理如下: Digester底層采用SAX(Simple API for XML)析XML文件,所以很自然的,對象轉換由"事件"驅動,在遍歷每個節點時,檢查是否有匹配模式,如果有,則執行規則定義的操作,比如創建特定的Java對象,或調用特定對象的方法等。此處的XML元素根據匹配模式(matching pattern)識別,而相關操作由規則(rule)定義。
如下xml代碼,右邊是左邊元素對應的匹配模式:
<datasources> 'datasources' <datasource> 'datasources/datasource' <name/> 'datasources/datasource/name' <driver/> 'datasources/datasource/driver' </datasource> <datasource> 'datasources/datasource' <name/> 'datasources/datasource/name' <driver/> 'datasources/datasource/driver' </datasource> </datasources>
例子1:
下面介紹解析xml文件的代碼
下面是存放地址及編碼的xml文件viewcache.xml(片段):
<?xml version="1.0" encoding="UTF-8" ?> <viewcache> <areas> <area> <id>1098</id> <parentId>1001</parentId> <areaType>province</areaType> <name>北京</name> <ordering>1867</ordering> </area> <area> <id>1099</id> <parentId>1098</parentId> <areaType>capital</areaType> <name>北京</name> <ordering>1868</ordering> <phoneArea>010</phoneArea> </area> <area> <id>4476</id> <parentId>1099</parentId> <areaType>county</areaType> <name>北京市朝陽區</name> <ordering>1869</ordering> <phoneArea>010</phoneArea> </area> <area> <id>4477</id> <parentId>1099</parentId> <areaType>county</areaType> <name>北京市崇文區</name> <ordering>1870</ordering> <phoneArea>010</phoneArea> </area> <area> <id>4478</id> <parentId>1099</parentId> <areaType>county</areaType> <name>北京市大興區</name> <ordering>1871</ordering> <phoneArea>010</phoneArea> </area> </areas> </viewcache>
此xml文件分3層結構,分別為:
<viewcache>節點 其下包含1個<areas>節點
<areas>節點 其下包含多個<area>節點
<area>節點,其下包含各種信息節點 : 如:<id> 、<name>等。
我們的操作目標是把area中的信息節點的內容提取出來。
把每個<arrea>看做為一個對象,<area>中信息節點的內容為對象中的元素。
設定一個類Area.java 其內容如下:
public class Area { private int id; private String name; private String areaType; private int parentId; private int ordering; private String zip; private String phoneArea; public int getOrdering() { return ordering; } public void setOrdering(int ordering) { this.ordering = ordering; } public String getAreaType() { return areaType; } public void setAreaType(String areaType) { this.areaType = areaType; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getParentId() { return parentId; } public void setParentId(int parentId) { this.parentId = parentId; } public String getZip() { return zip; } public void setZip(String zip) { this.zip = zip; } public String getPhoneArea() { return phoneArea; } public void setPhoneArea(String phoneArea) { this.phoneArea = phoneArea; } }
創建一個ViewCache類,用來保存解析后的所有對象:
public class ViewCache { private List areaList = new ArrayList(); public List getAreaList() { return areaList; } public void setAreaList(List areaList) { this.areaList = areaList; } // 供Digester調用的方法 public void addArea(Area area) { this.areaList.add(area); } }
創建一個類AreaDigester,對xml文件進行解析:
public class AreaDigester { public ViewCache digester() throws Exception { Digester digester = new Digester(); digester.setValidating(false); digester.addObjectCreate("viewcache/areas", ViewCache.class); // 指明匹配模式和要創建的類 digester.addObjectCreate("viewcache/areas/area", Area.class); // 設置對象屬性,與xml文件對應,不設置則是默認 digester.addBeanPropertySetter("viewcache/areas/area/id", "id"); digester.addBeanPropertySetter("viewcache/areas/area/parentId", "parentId"); digester.addBeanPropertySetter("viewcache/areas/area/name", "name"); digester.addBeanPropertySetter("viewcache/areas/area/areaType", "areaType"); digester.addBeanPropertySetter("viewcache/areas/area/ordering", "ordering"); digester.addBeanPropertySetter("viewcache/areas/area/zip", "zip"); digester.addBeanPropertySetter("viewcache/areas/area/phoneArea", "phoneArea"); // 當移動到下一個標簽中時的動作 digester.addSetNext("viewcache/areas/area", "addArea"); ViewCache vc = null; try { vc = (ViewCache) digester.parse("viewcache.xml"); } catch (IOException e) { throw new Exception(e); } catch (SAXException e) { throw new Exception(e); } return vc; } }
調用AreaDigester的digester方法,即可把解析后的所有地址對象,存放在ViewCache的list中。
例子2:
要解析的xml文件books.xml如下:
<?xml version="1.0" encoding="UTF-8" ?> <library name="alibaba圖書館"> <book title ="thinking in java" author="xxx"> <chapter> <no>第一章</no> <caption>第一章的標題</caption> </chapter> <chapter> <no>第二章</no> <caption>第二章的標題</caption> </chapter> </book> <book title="effective java" author="yyy"> <chapter> <no>第一章</no> <caption>第一章的標題</caption> </chapter> </book> </library>
Library類如下:
public class Library { private String name; private List<Book> bookList = new ArrayList<Book>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Book> getBookList() { return bookList; } public void addBook(Book book) { bookList.add(book); } }
Book類如下:
public class Book { private String title; private String author; private List<Chapter> chapters = new ArrayList<Chapter>(); /** * 這個方法,用來演示xml的解析時用的另一種方式 * @param title * @param author */ public void setBookInfo(String title, String author) { this.title = title; this.author = author; } public void addChapter(Chapter chapter) { this.chapters.add(chapter); } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public List<Chapter> getChapters() { return chapters; } public void setChapters(List<Chapter> chapters) { this.chapters = chapters; } }
Chapter類如下:
public class Chapter { private String no; private String caption; public String getNo() { return no; } public void setNo(String no) { this.no = no; } public String getCaption() { return caption; } public void setCaption(String caption) { this.caption = caption; } }
解析xml的類如下:
public class MainTest { /** * @param args */ public static void main(String[] args) { // 建立一個Digester對象 Digester digester = new Digester(); //指定它不要用DTD驗證XML文檔的合法性——這是因為我們沒有為XML文檔定義DTD digester.setValidating(false); // 從library標簽開始解析,并新建一個Library對象做為根對象 digester.addObjectCreate("library", Library.class); // 根據library標簽屬性值設置對象的屬性,一次可以設置多個屬性 digester.addSetProperties("library"); // 也可以用下面的方法,指定propertyName // digester.addSetProperties("library", "name", "name"); // -----第1層元素開始 digester.addObjectCreate("library/book", Book.class); //digester.addSetProperties("library/book"); // 可以用以下三條語句代替 digester.addCallMethod("library/book", "setBookInfo", 2); digester.addCallParam("library/book", 0, "title"); digester.addCallParam("library/book", 1, "author"); /** * addCallParam(String rule, int paraIndex,String attributeName) * 該方法與addCallMethod配合使用 * int paraIndex:表明需要填充的方法形參序號,從 0 開始,方法由addCallMethod指定 * String attributeName:指定標簽屬性名稱 */ // -----第2層元素開始 digester.addObjectCreate("library/book/chapter", Chapter.class); /** addBeanPropertySetter()是將子節點轉換為對象的屬性,這個方法還可以有第二個參數,當對象的屬性名和子節點的名字不一樣時用來指定對象的屬性名 該方法的作用及使用方法類似于addSetProperties,只不過它是用String rule規則所指定標簽的值(而不是標簽的屬性)來調用對象的setter*/ digester.addBeanPropertySetter("library/book/chapter/no"); // digester.addBeanPropertySetter("library/book/chapter/no", "no"); /** addCallMethod(String rule,String methodName, int paraNumber) 方法 * 同樣是設置對象的屬性,但是方式更加靈活,不需要對象具有setter * 當paraNumber = 0時,可以單獨使用(表明為標簽的值來調用),不然需要配合addCallParam方法 */ // digester.addBeanPropertySetter("library/book/chapter/caption"); // 下面的方法,可以用來代替上一句,作用是一樣的 digester.addCallMethod("library/book/chapter/caption", "setCaption", 0); // addSetNext()是說在再次遇到匹配節點后, 調用當前對象(Chapter類的對象)的父對象(Book類的對象)的方法,方法參數是當前層元素的對象 digester.addSetNext("library/book/chapter", "addChapter"); // -----第2層元素結束 digester.addSetNext("library/book", "addBook"); // -----第1層元素結束 try { // 解析XML文件,并得到ROOT元素 Library library = (Library) digester.parse(MainTest.class.getResourceAsStream("books.xml")); System.out.println(" 圖書館: " + library.getName()); System.out.println(" 共藏書: " + library.getBookList().size() + " 本 "); System.out.println(" ***************************** "); for (Book book : library.getBookList()) { System.out.println(" 書名: " + book.getTitle() + " 作者: " + book.getAuthor()); System.out.println(" ------------------------------ "); // 顯示章節 System.out.println(" 共 " + book.getChapters().size() + " 章 "); for (Chapter chapter : book.getChapters()) { System.out.println(chapter.getNo() + ": " + chapter.getCaption()); } System.out.println(" ------------------------------ "); } } catch (IOException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } } }
例子3:
Digester解析xml的規則,除了在java類中描述設置之外,還可以把解析規則放在xml文件中。以例子2中的代碼為例,規則在books-rule.xml文件中,內容如下:(The DTD is distributed in the commons-digester.jar
. It can be found at org/apache/commons/digester/xmlrules/digester-rules.dtd,通過查看DTD文件,可以知道有哪些標簽可以使用
)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE digester-rules PUBLIC "-//Jakarta Apache //DTD digester-rules XML V1.0//EN" "digester-rules.dtd"> <digester-rules> <object-create-rule pattern="library" classname="com.alibaba.chj.digester.Library" /> <set-properties-rule pattern="library"> <alias attr-name="name" prop-name="name" /> </set-properties-rule> <pattern value="library/book"> <object-create-rule classname="com.alibaba.chj.digester.Book" /> <set-properties-rule /> <pattern value="chapter"> <object-create-rule classname="com.alibaba.chj.digester.Chapter" /> <bean-property-setter-rule pattern="no" propertyname="no" /> <bean-property-setter-rule pattern="caption" propertyname="caption" /> <set-next-rule methodname="addChapter" /> </pattern> <set-next-rule methodname="addBook" /> </pattern> </digester-rules>
解析xml類的代碼,修改為:
public class MainTest { /** * @param args */ public static void main(String[] args) { try { Digester digester = DigesterLoader.createDigester(DigesterXmlRuleTest.class.getResource("books-rule.xml")); Library library = (Library) digester.parse(DigesterXmlRuleTest.class.getResourceAsStream("books.xml")); System.out.println(" 圖書館: " + library.getName()); System.out.println(" 共藏書: " + library.getBookList().size() + " 本 "); System.out.println(" ***************************** "); for (Book book : library.getBookList()) { System.out.println(" 書名: " + book.getTitle() + " 作者: " + book.getAuthor()); System.out.println(" ------------------------------ "); // 顯示章節 System.out.println(" 共 " + book.getChapters().size() + " 章 "); for (Chapter chapter : book.getChapters()) { System.out.println(chapter.getNo() + ": " + chapter.getCaption()); } System.out.println(" ------------------------------ "); } } catch (IOException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } } }
用于規則放在xml文件中,所以解析的類,顯得更加簡潔一些。