快速與簡(jiǎn)單并非天生不可兼得, 相反, 簡(jiǎn)單的東西應(yīng)該是快速的
在使用 SAX 解析 XML 的過(guò)程中, 碰到了以下問(wèn)題:
- SAX Handler 并沒(méi)有想象中快, 尤其是文件比較大的時(shí)候
- SAX Handler 編寫(xiě)容易出錯(cuò), 因?yàn)樾枰獏^(qū)別不同的元素, 需要很多判斷才能拿到自己想要的信息
- 沒(méi)有統(tǒng)一的方法獲取SAX Handler解析出來(lái)的信息
這實(shí)際上反映了目前SAX Handler接口定義中缺失的三種能力:
Stoppable,
Subscribable, 和
Reportable
1, Stoppable
缺省情況下SAX Parser會(huì)解析整個(gè)文件, 即使你已經(jīng)取得了足夠的你想要的信息, 但解析不會(huì)停止, 這就是感覺(jué)SAX Parser在解析大文件的時(shí)候不是很快的原因
只有異常才能阻止SAX Parser繼續(xù)解析, 所以解決方法很簡(jiǎn)單:
a). 定義接口:
public interface Stoppable {
boolean canStop();
}
b). 缺省實(shí)現(xiàn):
public abstract class EnhancedHandler extends DefaultHandler implements Reportable {
private boolean canStop;
public boolean canStop() { return canStop; }
protected void stop() { canStop = true; } //call this method whensubclass objects get enough information.
}
c). 當(dāng)且只當(dāng)所有SAX Handler都可以停止的時(shí)候, 拋出異常:
public class CompositeEnhancedHandler extends DefaultHandler {
private static final RuntimeException SHOULD_STOP_EXCEPTION = newShouldStopParsingException();
private final EnhancedHandler[] handlers;
publicCompositeEnhancedHandler(EnhancedHandler... handlers) {
this.handlers = handlers;
}
public void characters(char[] ch, int start, int length) throws SAXException {
for (EnhancedHandler handler : handlers) { handler.characters(ch, start,length); }
throwExceptionIfCanStop();
}
public void endElement(String uri, String localName,String qName) throws SAXException {
for (EnhancedHandler handler : handlers) { handler.endElement(uri, localName,qName); }
throwExceptionIfCanStop();
}
public void startElement(String uri, String localName,String qName, Attributes attributes) throws SAXException {
for (EnhancedHandler handler : handlers) { handler.startElement(uri,localName, qName, attributes); }
throwExceptionIfCanStop();
}
private void throwExceptionIfCanStop() {
for (EnhancedHandler handler : handlers) { if (!handler.canStop()) { return; } }
throw SHOULD_STOP_EXCEPTION;
}
}
d). SAX Parser 捕獲異常:
CompositeEnhancedHandler handler = newCompositeEnhancedHandler(new Handler1(), new Handler2());
try {
SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
saxParser.parse(new File("england.xml"), handler);
} catch (ShouldStopParsingException se) {
// All handlers got enough information, just stop parsing.
}
2. Subscribable
不能指定只處理特定元素的能力的缺乏, 使得SAX Handler難以編寫(xiě)且易于出錯(cuò), 不得不判斷當(dāng)前元素的名稱, 是否正在處理特定的元素等, 這使得每個(gè)Handler都在重復(fù)這些邏輯相似的代碼.
解決方法是提供一個(gè)額外的中間層, 詢問(wèn)SAX Handler對(duì)哪個(gè)元素感興趣. 該中間層只會(huì)向每個(gè)SAX Handler發(fā)送它們感興趣的元素信息. (也可以采用每個(gè)SAX Handler向中間層注冊(cè)感興趣信息的方法, 但比較復(fù)雜, ESAX采用前者)
a). 定義接口:
public interface Subscribable {
String subscribe();
}
b). 中間層 CompositeEnhancedHandler:
public class CompositeEnhancedHandler extends DefaultHandler{
private final AddableMap mapping = new AddableMap();
private List<EnhancedHandler> currentHandlers;
publicCompositeEnhancedHandler(EnhancedHandler... handlers) {
... ...
for (EnhancedHandler handler : handlers) { mapping.get(handler.subscribe()).add(handler); }
}
public void startElement(String uri, String localName,String qName, Attributes attributes) throws SAXException {
currentHandlers = mapping.get(qName);
for (EnhancedHandler handler : currentHandlers) { handler.startElement(uri,localName, qName, attributes); }
... ...
}
public void characters(char[] ch, int start, int length) throws SAXException {
for (EnhancedHandler handler : currentHandlers) { handler.characters(ch, start,length); }
... ...
}
public void endElement(String uri, String localName,String qName) throws SAXException {
for (EnhancedHandler handler : currentHandlers) { handler.endElement(uri, localName,qName); }
... ...
}
private static class AddableMap {
private Map<String, List<EnhancedHandler>>container = new HashMap<String, List<EnhancedHandler>>();
public List<EnhancedHandler>get(String qname) {
if (!container.containsKey(qname)) { container.put(qname, new ArrayList<EnhancedHandler>()); }
return container.get(qname);
}
}
}
3. Reportable
DOM提供了很方便的方法供提取特定信息, 但SAX Handler缺失了這項(xiàng)能力, 感興趣的信息被藏在每個(gè)Handler內(nèi)部
ESAX提供的解決方法是"收集參數(shù)模式"
a). 定義接口:
public interface Reportable {
void report(Map resultSet);
}
b). 缺省支持:
public abstract class EnhancedHandler extends DefaultHandler implements Reportable,Stoppable, Subscribable {
... ...
}
public class CompositeEnhancedHandler extends DefaultHandler implements Reportable{
public void report(Map resultSet) {
for (EnhancedHandler handler : handlers) { handler.report(resultSet); }
}
}
最終, ESAX 為 原始的 SAX Handler 補(bǔ)足了 可中止的能力, 可訂閱的能力, 可匯報(bào)的能力, 使得比原始的SAX Handler更快, 比DOM接口更簡(jiǎn)單, 更易于編程
一個(gè)簡(jiǎn)單的例子可參見(jiàn):
http://jade-stone-suite.googlecode.com/svn/trunk/JS.ESax/test/jade/stone/esax/sample/FACupHandler.java測(cè)試用例參見(jiàn):
http://jade-stone-suite.googlecode.com/svn/trunk/JS.ESax/test/jade/stone/esax/test/CompositeEnhancedHandlerTest.java最終的缺省實(shí)現(xiàn)可參見(jiàn):
http://jade-stone-suite.googlecode.com/svn/trunk/JS.ESax/src/jade/stone/esax/support/EnhancedHandler.javahttp://jade-stone-suite.googlecode.com/svn/trunk/JS.ESax/src/jade/stone/esax/support/CompositeEnhancedHandler.java項(xiàng)目主頁(yè):
http://jade-stone-suite.googlecode.com/svn/trunk/JS.ESax/