2010-08-04 155 views
7

我目前使用SAX(Java)來解析少數不同的XML文檔,每個文檔都代表不同的數據,並且結構略有不同。由於這個原因,每個XML文檔都由不同的SAX類處理(子類DefaultHandler)。使用SAX解析常用XML元素

但是,有一些XML結構可以出現在所有這些不同的文檔中。理想情況下,我想告訴解析器「嗨,當你到達一個complex_node元素時,只需使用ComplexNodeHandler來讀取它,並將結果返回給我。如果達到some_other_node,請使用OtherNodeHandler來讀取它並讓我知道結果」。

但是,我看不到一個明顯的方法來做到這一點。

我應該只是做一個單片處理程序類,它可以讀取我擁有的所有不同文檔(並根除代碼重複),還是有更智能的方法來處理?

+0

我希望/確定我剛剛錯過了一些痛苦明顯的解決方案! – Dave 2010-08-04 12:58:08

+0

是SAX的要求嗎?如何使用xpath與DOM,XOM或vtd-xmL? – 2011-03-01 21:33:47

+0

因爲SAX是最快的並且使用最少的內存,這在移動設備上很重要(當我最初問這個問題時,我忽略了這一點)。 – Dave 2011-03-01 21:54:35

回答

11

下面是我對類似問題的一個答案(Skipping nodes with sax)。它演示瞭如何交換XMLReader上的內容處理程序。

在這個例子中,ContentHandler中的交換隻是忽略了所有的事件,直到它放棄控制,但是你可以很容易地修改這個概念。


你可以做類似如下:

import javax.xml.parsers.SAXParser; 
import javax.xml.parsers.SAXParserFactory; 
import org.xml.sax.XMLReader; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     SAXParserFactory spf = SAXParserFactory.newInstance(); 
     SAXParser sp = spf.newSAXParser(); 
     XMLReader xr = sp.getXMLReader(); 
     xr.setContentHandler(new MyContentHandler(xr)); 
     xr.parse("input.xml"); 
    } 
} 

MyContentHandler

這個類是負責處理XML文檔。當你點擊一個你想忽略的節點時,你可以交換IgnoringContentHandler,它將吞下該節點的所有事件。

import org.xml.sax.Attributes; 
import org.xml.sax.ContentHandler; 
import org.xml.sax.Locator; 
import org.xml.sax.SAXException; 
import org.xml.sax.XMLReader; 

public class MyContentHandler implements ContentHandler { 

    private XMLReader xmlReader; 

    public MyContentHandler(XMLReader xmlReader) { 
     this.xmlReader = xmlReader; 
    } 

    public void setDocumentLocator(Locator locator) { 
    } 

    public void startDocument() throws SAXException { 
    } 

    public void endDocument() throws SAXException { 
    } 

    public void startPrefixMapping(String prefix, String uri) 
      throws SAXException { 
    } 

    public void endPrefixMapping(String prefix) throws SAXException { 
    } 

    public void startElement(String uri, String localName, String qName, 
      Attributes atts) throws SAXException { 
     if("sodium".equals(qName)) { 
      xmlReader.setContentHandler(new IgnoringContentHandler(xmlReader, this)); 
     } else { 
      System.out.println("START " + qName); 
     } 
    } 

    public void endElement(String uri, String localName, String qName) 
      throws SAXException { 
     System.out.println("END " + qName); 
    } 

    public void characters(char[] ch, int start, int length) 
      throws SAXException { 
     System.out.println(new String(ch, start, length)); 
    } 

    public void ignorableWhitespace(char[] ch, int start, int length) 
      throws SAXException { 
    } 

    public void processingInstruction(String target, String data) 
      throws SAXException { 
    } 

    public void skippedEntity(String name) throws SAXException { 
    } 

} 

IgnoringContentHandler

當IgnoringContentHandler做吞嚥控制向後傳遞到主ContentHandler事件。

import org.xml.sax.Attributes; 
import org.xml.sax.ContentHandler; 
import org.xml.sax.Locator; 
import org.xml.sax.SAXException; 
import org.xml.sax.XMLReader; 

public class IgnoringContentHandler implements ContentHandler { 

    private int depth = 1; 
    private XMLReader xmlReader; 
    private ContentHandler contentHandler; 

    public IgnoringContentHandler(XMLReader xmlReader, ContentHandler contentHandler) { 
     this.contentHandler = contentHandler; 
     this.xmlReader = xmlReader; 
    } 

    public void setDocumentLocator(Locator locator) { 
    } 

    public void startDocument() throws SAXException { 
    } 

    public void endDocument() throws SAXException { 
    } 

    public void startPrefixMapping(String prefix, String uri) 
      throws SAXException { 
    } 

    public void endPrefixMapping(String prefix) throws SAXException { 
    } 

    public void startElement(String uri, String localName, String qName, 
      Attributes atts) throws SAXException { 
     depth++; 
    } 

    public void endElement(String uri, String localName, String qName) 
      throws SAXException { 
     depth--; 
     if(0 == depth) { 
      xmlReader.setContentHandler(contentHandler); 
     } 
    } 

    public void characters(char[] ch, int start, int length) 
      throws SAXException { 
    } 

    public void ignorableWhitespace(char[] ch, int start, int length) 
      throws SAXException { 
    } 

    public void processingInstruction(String target, String data) 
      throws SAXException { 
    } 

    public void skippedEntity(String name) throws SAXException { 
    } 

} 
+0

嗯,沒有意識到XMLReader可以通過這種方式進行即時更改。絕對看起來像是最好的方式來處理它。 – Dave 2010-08-05 08:09:40

+0

XMLReader被設計成可以做到這一點,請參考http://download-llnw.oracle.com/javase/6/docs/api/org/xml/sax/XMLReader.html#setContentHandler(org.xml.sax.ContentHandler ),在做SAX處理時,我們在我們的JAXB實現MOXy中使用了這個,我們爲每個對象構建一個ContentHandler。 – 2010-08-05 13:17:18

+0

@Blaise Doughan首先感謝您的解決方案,這正是我一直在尋找的。我有一個問題。在評估結構深度知道何時返回主內容處理程序的背後是否有任何特殊想法?爲此目的使用endDocument()方法有任何問題嗎? – 2010-09-20 10:20:06

0

您可以擁有一個只處理文檔某些部分(complex_node)的處理程序(ComplexNodeHandler),並將所有其他部分傳遞給另一個處理程序。 ComplexNodeHandler的構造函數會將另一個處理程序作爲參數。我的意思是這樣的:

class ComplexNodeHandler { 

    private ContentHandler handlerForOtherNodes; 

    public ComplexNodeHandler(ContentHandler handlerForOtherNodes) { 
     this.handlerForOtherNodes = handlerForOtherNodes; 
    } 

    ... 

    public startElement(String uri, String localName, String qName, Attributes atts) { 
     if (currently in complex node) { 
      [handle complex node data] 
     } else { 
      // pass the event to the document specific handler 
      handlerForOtherNodes.startElement(uri, localName, qName, atts); 
     } 
    } 

    ... 

} 

可能有更好的替代品,因爲我不熟悉SAX。編寫公共部分的基本處理程序並繼承它可能會起作用,但我不確定在這裏使用繼承是個好主意。

+0

我考慮過這個,但很快就確定它會變得相當複雜。我不得不轉發來自不僅僅是'startElement',而是'endElement','''字符'和錯誤處理程序的調用。 – Dave 2010-08-05 08:08:32