標籤:
在Java編程中,設定檔大多數都是用xml檔案來組織的。所以在Java語言中處理xml的工具就特別多。
在java中解析XML的幾種方式,應該都是知道的。在這些解析技術的基礎之上,又發展了幾種優秀Object/XML關聯的技術,例如有一種對象綁定技術(JAXB),再例如Digester。這裡就來簡單的瞭解一下Digester技術。
如果說對Digester運用最為高超的應用,非Tomcat莫屬了。Tomcat中幾個重要的設定檔(如server.xml、web.xml、context.xml),都是使用Digester來完成xml到Java對象的轉換的。
Digester只需要設定好相應的處理規定,就可以得到想要的對象結構。它的最大優點就是可以自訂各種處理規則。然而,如果不能明白Digester的設計原理,那你就只使用Digester給你提供的幾種規則了。
Digester例子
Java對象設計:
package com.fjn.frame.digester.list;public class XxObject { private String id; private String name; private int num; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } @Override public String toString() { return "id: " + id + "\tname: " + name + "\tnum: " + num; }}
這個類沒有任何的含義,只是我隨便寫的而已。
一個簡單的XML檔案:
<?xml version="1.0" encoding="UTF-8"?><XxObjects> <XxObject id="id001" name="hello1" num="1" /> <XxObject id="id002" name="hello2" num="2" /> <XxObject id="id003" name="hello3" num="3" /> <XxObject id="id004" name="hello4" num="4" /> <XxObject id="id005" name="hello5" num="5" /> <XxObject id="id006" name="hello6" num="6" /> <XxObject id="id007" name="hello7" num="7" /> <XxObject id="id008" name="hello8" num="8" /> <XxObject id="id009" name="hello9" num="9" /></XxObjects>
接下來的任務就是將這個XML檔案轉換為一個對象集合了。
package com.fjn.frame.digester.list;import java.io.IOException;import java.util.List;import javax.xml.parsers.FactoryConfigurationError;import javax.xml.parsers.ParserConfigurationException;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;import org.apache.commons.digester3.Digester;import org.junit.Test;import org.xml.sax.SAXException;public class Convertor { @Test public void test() throws ParserConfigurationException, SAXException, FactoryConfigurationError, IOException { SAXParser parser = SAXParserFactory.newInstance().newSAXParser(); Digester digester = new Digester(parser); digester.addObjectCreate("XxObjects", "java.util.LinkedList"); digester.addObjectCreate("XxObjects/XxObject", "com.fjn.frame.digester.list.XxObject"); digester.addSetProperties("XxObjects/XxObject"); // digester.addSetNext("XxObjects/XxObject", "add"); digester.addRule("XxObjects/XxObject", new AddToCollectionRule()); List<XxObject> ret = digester.parse(Convertor.class .getResourceAsStream("XxObjects.xml")); for (XxObject obj : ret) { System.out.println(obj); } }}
AddToCollectionRule規則是我自訂的規則:
package com.fjn.frame.digester.list;import java.util.Collection;import org.apache.commons.digester3.Rule;import org.xml.sax.Attributes;public class AddToCollectionRule extends Rule { @Override public void begin(String namespace, String name, Attributes attributes) throws Exception { Object top = this.getDigester().peek(); Object o = this.getDigester().peek(1); if (o != null) { Collection colls = (Collection) o; System.out.println(top); colls.add(top); } }}
程式運行結果:
id: id001name: hello1num: 1id: id002name: hello2num: 2id: id003name: hello3num: 3id: id004name: hello4num: 4id: id005name: hello5num: 5id: id006name: hello6num: 6id: id007name: hello7num: 7id: id008name: hello8num: 8id: id009name: hello9num: 9id: id001name: hello1num: 1id: id002name: hello2num: 2id: id003name: hello3num: 3id: id004name: hello4num: 4id: id005name: hello5num: 5id: id006name: hello6num: 6id: id007name: hello7num: 7id: id008name: hello8num: 8id: id009name: hello9num: 9
使用Digester將xml轉為java對象,就是這麼簡單。
使用起來如此簡單,它是怎麼做到的呢?
Digester設計原理
如果是使用DOM解析的方式,我相信只要你瞭解DOM樹,你就肯定能夠完成這個輕鬆的任務,這也是程式員們更加容易接受DOM解析的原因。但是,設想一下,如果讓你來將一個XML檔案使用SAX解析的方式,轉換為Java對象,你會怎麼做呢?
我在看Digester的源碼之前,就做過這樣的設想。因為SAX解析的方式中,我們使用的最多的應當是startElement和endElement了。SAX解析是基於事件的,遇到一個開始元素,就執行startElement方法,遇到一個結束元素就執行endElement方法。
一般來說一個XML元素就對應一個Java對象,XML元素的屬性就對應的是Java對象的屬性。遇到一個元素開始,就可以根據元素名稱來建立出對象。根據元素的屬性來設定對象的屬性。這個了是很簡單的,但是在end一個元素後,也就是對象建立完成後,怎麼來儲存呢?總不能解析完成,產生了很多個物件,我一個也拿不到,那解析它有何用呢。可以考慮一個複雜的XML,例如tomcat的server.xml檔案,Server\Service\Engine等有對象是嵌套的,建立一個對象Service後,怎麼將它設定到Server中呢?建立一個對象Engine後,怎麼將它設定到Service中呢。完成這個工作,就必然要使用到一個資料結構:Stack。如果你能想到這裡,Digester你就學會了80%了。
Java對象在startElement中建立併入棧,在endElement中完成所有操作並出棧。
例子說明
在上面的例子中,解析到根項目時,會建立出一個LinkedList,然後入棧。因為沒有到結束元素,所以它肯定會在棧裡,並且是在最下面。然後是解析每個XxObject了。在解析XxObject元素時,都要有3個操作,它們是依次進行的:
1)建立XxObject對象。入棧,那麼這個對象肯定是在棧頂了。
2)設定屬性。Digester使用的是BeanUtils工具來完成屬性的設定的,所以java類在設計時, 是需要 getter和setter的。
3)調用我自訂的AddToCollection規則了。執行這個規則也很簡單,取得當前對象(棧頂元素),再取出從棧頂起第二個元素,也就是LinkedList。然後就可以添加到集合中了。
4)endElement,對象出棧,棧中只剩下LinkedList對象。
最後遇到XxObejcts的end,它就是解析到最後一刻時棧底。返回的什就是棧底對象。所以結果就是一個List了。
我自訂的那個AddToCollection規則,只是想用於說明如何Digester的原理,以及如何使用自訂規則。其實像這個的常用的規則,Digester已經定義好了,SetNetRule。
到這裡,這篇文章的主要內容已經說完了。至於如何使用Digester中的各種Rule,還需要靠自己去瞭解了。
下面附加一張類圖:
Digester : a java object builder based xml