KXML是一個(gè)只占很小存儲(chǔ)空間的XML語法分析程序,對于J2ME應(yīng)用程序非常適合。它有一個(gè)非常獨(dú)特的DOM操作方法和被稱為Pull的語法分析方法。
KXML是一個(gè)被設(shè)計(jì)用于J2ME設(shè)備的簡化類庫,雖然它也可以被用于其它需要小型XML語法分析程序的環(huán)境,比如Applet。KXML是一個(gè)Enhydra維護(hù)的項(xiàng)目,支持下面的性能:
· 支持XML名稱空間
· 用"松散"模式分析HTML或其它SGML格式
· 占用很少的存儲(chǔ)空間(21 kbps)
· 基于Pull的分析
· 支持XML寫操作
· 可選的DOM支持
· 可選的WAP支持
在本文中,我將詳細(xì)說明其中的一些特點(diǎn),尤其是Pull分析和DOM操作,而且我將告訴你如何檢查KXML在內(nèi)存中操作的效果
使用XML工作
有兩個(gè)常見的使用XML工作的方法:操作DOM或者捕捉語法分析事件。操作DOM是一個(gè)與XML相互作用的簡單方法,通常這個(gè)XML是一棵完整的XML樹,被解析成一個(gè)存放在存儲(chǔ)器中的節(jié)點(diǎn)結(jié)構(gòu),你可以遍歷這棵樹。它非常簡單易用,但是因?yàn)檎脴浯嬖谟诖鎯?chǔ)器中造成存儲(chǔ)器的負(fù)擔(dān)。
第二種方法在捕捉語法分析事件中,每當(dāng)語法分析程序遇到數(shù)據(jù)中的特定結(jié)構(gòu),它就會(huì)遍歷XML數(shù)據(jù),然后把結(jié)果發(fā)回前面注冊的一個(gè)事件監(jiān)聽器中。比如說,當(dāng)語法分析程序遇到一個(gè)起始標(biāo)記,如<html>,那么事件監(jiān)聽器將接收一個(gè)事件,通知它這個(gè)情況,并且向它傳遞任何所需的信息。實(shí)現(xiàn)這種策略的語法分析程序被稱為push語法分析程序,因?yàn)檫@個(gè)語法分析程序把事件"推入"一個(gè)監(jiān)聽器中。
KXML支持DOM語法分析和操作,但是不支持push語法分析。取而代之,它使用一種稍微不同的稱為"Pull"的分析方法。與push語法分析相反,Pull語法分析讓程序員從語法分析程序中"拉"出下一個(gè)事件。在push語法分析中,你必須維護(hù)你正在分析的當(dāng)前數(shù)據(jù)的狀態(tài),然后基于傳送到監(jiān)聽器的事件,恢復(fù)任何以前的狀態(tài),并且當(dāng)你轉(zhuǎn)換到一個(gè)不同的狀態(tài)時(shí)保存新的狀態(tài)。Pull語法分析使處理狀態(tài)改變更加容易,因?yàn)槟憧梢园l(fā)送分析器到不同的函數(shù),維護(hù)它們自己的狀態(tài)變量。
Pull語法分析
讓我們來研究一個(gè)例子,看看KXML如何做一個(gè)Pull語法分析程序。演示程序名為KXMLDemo_Pull。它將使用一個(gè)Pull語法分析程序查看一個(gè)包含通訊錄信息的文件。下面給出源代碼中比較重要的幾行,我還給出了注釋。
1.XmlParser parser = null; 2...... 3.parser = new XmlParser( new InputStreamReader( 1this.getClass().getResourceAsStream(resfile_name) )); |
第三行創(chuàng)建了一個(gè)XmlParser,把它傳到一個(gè)InputStream中。這個(gè)語法分析程序反復(fù)調(diào)用,直到出現(xiàn)END_DOCUMENT事件。
1.while ( (event = parser.read()).getType() != Xml.END_DOCUMENT ) { 2. ... 3.if (name != null && name.equals("address")) { 4. ... 5. parseAddressTag( parser ); |
第三行判斷事件是否以一個(gè)<address>標(biāo)記開始,第五行傳送語法分析器到控制語法分析程序的"parseAddressTag"。
1.while ((event = parser.peek()).getType() != Xml.END_DOCUMENT) { 2.... 3. if (type == Xml.END_TAG && name.equals("address")) { 4. return; 5. } 6.... 7. ParseEvent next = parser.read(); 8. 9. // if it's not a text event then skip it 10. if (next.getType() != Xml.TEXT) { 11. continue; 12. } 13.... 14. System.err.println(name + ": " + text); |
上面的這段代碼在"parseAddressTag"中循環(huán),直到找到與<address>對應(yīng)的終止標(biāo)記。如果它遇到其它任何標(biāo)記,那么標(biāo)記名和標(biāo)記內(nèi)容就會(huì)被打印到控制臺(tái)上。因此,如果找到標(biāo)記<name>Robert Cadena</name>,你將看到下面的控制臺(tái)輸出:
name: Robert Cadena
一旦找到<address>的終止標(biāo)記(8- 10行),控件被返回調(diào)用函數(shù),然后又開始查找<address>。
如你所見,使用Pull語法分析程序非常容易,并且能夠傳送語法分析程序到另一個(gè)函數(shù),然后在文檔中查找元素。你并不局限于分析資源文件;你還可以使用HttpConnection把這個(gè)函數(shù)傳遞到http InputStream。這把你從讀取InputStream、保存內(nèi)容、分析內(nèi)容等操作中解放了出來,一切都由KXML為你完成。
DOM處理
Pull語法分析特別適用于當(dāng)你需要維護(hù)非常小的存儲(chǔ)空間的時(shí)候,因?yàn)榘l(fā)出事件的文檔只有一部分存在于內(nèi)存中。換句話說,如果你感興趣的特定數(shù)據(jù)段是文檔中部的幾百個(gè)字節(jié),那么前面的幾百個(gè)字節(jié)就不必保存在內(nèi)存中了。
但是如果你能夠節(jié)省一些內(nèi)存,你可以使用另一個(gè)版本的KXML語法分析程序,它包含對DOM的支持。 DOM是保存在內(nèi)存中的整個(gè)文檔樹,每個(gè)標(biāo)記都被分離成節(jié)點(diǎn)(Node)對象。 你可以遍歷這個(gè)文檔樹,然后根據(jù)需要取得數(shù)據(jù)。
工程中的另一個(gè)MIDlet,KXMLDemo_dom,做了同樣的事情。它讀取一個(gè)通訊錄,然后把內(nèi)容打印到控制臺(tái),但是這次它使用了DOM。下面給出源代碼中比較重要的幾行.
1.Document doc = new Document(); 2.... 3.parser = new XmlParser( isr ); 4.doc.parse( parser ); |
第一行創(chuàng)建了一個(gè)文檔,保存XML樹。第三行從一個(gè)名為isr的InputStreamReader中創(chuàng)建一個(gè)KXML語法分析程序。第四行傳送這個(gè)語法分析程序到文檔,然后讓文檔開始分析。XML被遞歸分析,直到到達(dá)文檔的結(jié)尾。當(dāng)分析調(diào)用退出時(shí),整個(gè)文檔被裝入內(nèi)存,這時(shí)你就可以操作它了。
1.Element root = doc.getRootElement(); 2.int child_count = root.getChildCount(); 3.... 4.for (int i = 0; i < child_count ; i++ ) { 5.... 6. Element kid = root.getElement(i); 7. 8. if (!kid.getName().equals("address")) { 9. continue; 10. } |
因?yàn)槲覀冎溃糰ddress>元素是根元素的直接子元素,我們可以遍歷根元素的子元素,尋找address標(biāo)記,如果子元素不是一個(gè)address 標(biāo)記,則返回。
1.int address_item_count = kid.getChildCount(); 2. 3. for (int j = 0; j < address_item_count ; j++) { 4.... |
如果我們找到了address子元素,我們開始遍歷它的子元素,并把這些子元素的內(nèi)容打印出來。不幸的是,你不能只是使用kid.getElement("name"),因?yàn)槿绻@個(gè)元素不存在的話,那么你將得到一個(gè)RuntimeException。所以我建議只有當(dāng)你知道XML文檔中存在你所有需要的所有字段時(shí)才使用這個(gè)方法。