剛剛學了一下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層結構,分別爲:
節點 其下包含1個節點
節點 其下包含多個節點
節點,其下包含各種信息節點 : 如: 、等。
我們的操作目標是把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的類如下:
[java] view plain copy
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文件中,所以解析的類,顯得更加簡潔一些。
Digester FAQ: http://wiki.apache.org/commons/Digester/FAQ