DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> XML學習教程 >> XML詳解 >> 為 JDOM 解析 XML 文件成 Document 加速
為 JDOM 解析 XML 文件成 Document 加速
編輯:XML詳解     

我們用 JDOM 解析 XML 最簡單的代碼莫過於以下兩行代碼,不過為了測試我們在其前後加上記錄執行時間的代碼:

long start = System.currentTimeMillis(); 
SAXBuilder builder = new SAXBuilder(); 
Document document = builder.build("struts-config.XML"); 
System.out.println("耗時:" + (System.currentTimeMillis()-start)+" 毫秒."); 
long start = System.currentTimeMillis();
SAXBuilder builder = new SAXBuilder();
Document document = builder.build("struts-config.XML");
System.out.println("耗時:" + (System.currentTimeMillis()-start)+" 毫秒.");

  在這個 struts-config.XML 中的 DTD 聲明如下:

<!DOCTYPE struts-config PUBLIC "-//apache Software Foundation//DTD Struts Configuration 1.3//EN" 
                "http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd"> 
<!DOCTYPE struts-config PUBLIC "-//apache Software Foundation//DTD Struts Configuration 1.3//EN"
                "http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd">

  正常執行時,打印出的耗時是 2698 毫秒(五次的平均值),這是能正常訪問 http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd 的情況下。假如把網線拔了,再執行上面的代碼,就會報出下面的異常:

Exception in thread "main" Java.Net.UnknownHostException: jakarta.apache.org
at Java.Net.PlainSocketImpl.connect(PlainSocketImpl.Java:177)
.......................................................
at sun.Net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.Java:977)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(XMLEntityManager.Java:677)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startEntity(XMLEntityManager.Java:1315)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.startDTDEntity(XMLEntityManager.Java:1282)
at com.sun.org.apache.xerces.internal.impl.XMLDTDScannerImpl.setInputSource(XMLDTDScannerImpl.Java:283)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl$DTDDriver.dispatch(XMLDocumentScannerImpl.Java:1176)
........................................................

很明顯,前面的代碼要從網絡上讀取 struts-config_1_3.dtd 來進行驗證,於是有了第一個加速的辦法:本地 DTD 驗證。從本地讀取 struts-config_1_3.dtd 文件,從 http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd 下載 struts-config_1_3.dtd 放到 struts-config.xml 同一目錄。然後修改 struts-config.XML 的 DTD 聲明如下:

<!DOCTYPE struts-config PUBLIC "-//apache Software Foundation//DTD Struts Configuration 1.3//EN" 
                "struts-config_1_3.dtd"> 
<!DOCTYPE struts-config PUBLIC "-//apache Software Foundation//DTD Struts Configuration 1.3//EN"
                "struts-config_1_3.dtd">

  再執行上面的代碼,打印出的耗時是 717 毫秒(五次的平均值),比前面的 2698 節省了 73.4 的時間,我所用的網絡帶寬也是能 BT 到 250 K的那種。

  前面兩種情況都是進行了 DTD 驗證的情形,如果我們能給予 XML 充分信任時,就可以不進行 DTD 驗證,這是一種極端,這時候管不管你有沒有接網線都不在乎了。因此這第二種辦法就是 不進行 DTD 驗證。

  查了一下 SAXBuilder 的 API http://www.jdom.org/docs/apidocs/org/jdom/input/SAXBuilder.Html,有構造方法 SAXBuilder(boolean validate) 和一個實例方法 setValidation(boolean validate) 。望文生義,似乎把參數設置為 false,就能合乎不進行 DTD 驗證的要求,可是錯了,validate 的默認值就是 false。SAXBuilder 還有一個方法 setDTDHandler(org.XML.sax.DTDHandler dtdHandler) 好像也是干這事的,於是試了一下讓 DTDHandler 無所作為:

 

builder.setDTDHandler(new DTDHandler(){ 
  public void notationDecl(String name, String publicId, 
      String systemId) throws SAXException { 
  } 
  public void unparsedEntityDecl(String name, String publicId, 
      String systemId, String notationName) throws SAXException { 
  } 
}); 
builder.setDTDHandler(new DTDHandler(){
 public void notationDecl(String name, String publicId,
  String systemId) throws SAXException {
 }
 public void unparsedEntityDecl(String name, String publicId,
  String systemId, String notationName) throws SAXException {
 }
});

  可執行效果仍和本地 DTD 驗證是一樣的。也許早有人曾納悶一下前面貼出的異常為何有選擇性的。是的,關注一下中間那段 Entity 的處理。為了不進行 DTD 驗證,我們需要為 SAXBuilder 設置一個自定義的 EntityResolver。不進行 DTD 驗證的完整代碼如下:

long start = System.currentTimeMillis(); 
SAXBuilder builder = new SAXBuilder(); 
builder.setEntityResolver(new EntityResolver(){ 
  public InputSource resolveEntity(String publicId, String systemId) { 
    return new InputSource(new StringReader("")); 
  } 
}); 
Document document = builder.build("struts-config.XML"); 
System.out.println("耗時:" + (System.currentTimeMillis()-start)+" 毫秒."); 
long start = System.currentTimeMillis();
SAXBuilder builder = new SAXBuilder();
builder.setEntityResolver(new EntityResolver(){
 public InputSource resolveEntity(String publicId, String systemId) {
 return new InputSource(new StringReader(""));
 }
});
Document document = builder.build("struts-config.XML");
System.out.println("耗時:" + (System.currentTimeMillis()-start)+" 毫秒.");

  現在執行上面這段代碼,打印出的耗時為 608 毫秒(五次平均值),比之 717 略有改善,不甚明顯。但有一個最大的好處是不用下載 DTD 文件至本地,繼而修改 XML 文件本身。

  我時常寫起東西來,總愛循著自己的思路脈絡來寫,而非開門見山的給出答案。所以一不小心就堆出個有些走樣的長篇累牍。很考驗讀者的耐心,非得翻到最後才能知曉個究竟,估計很多人在中途被嚇跑。看過《辛德勒的名單》的朋友一定會有這樣的感受,前面 2 個多小時都是十分的沉悶,到最後部分才漸入佳境,很扣人心弦的。不太恰當的比喻,我還炮制不出這種作品來。

  好啦,中間閉話了,總結一下,實際上前面那麼多加速 JDOM 解析 XML 的文字,兩言以蔽之就是:

  方法 1:把 XML 中的 DTD 文件下載至本地,並修改該 XML,使之應用本地的那個 DTD 文件。網絡驗證改為本地驗證效果很明顯。

  方法 2:只需為 SAXBuilder 對象設置一個返回 new InputSource(new StringReader("")) 的 EntityResolver 即可。但要自己保證 XML 的合法性了。

  如果是在只有少量的 XML 文件要解析,或只在程序啟動時加載 XML 的應用中,大可不必動此干戈。而在以解析 XML 文件較為密集的應用中,這種加速就是個善舉了。比如我先前做的一個 StrutsConfigHelper,專為解析多個 struts-config.XML,然後從中查找相應配置,如果仍從網絡驗證那就很難接受了。


XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved