Использование SAX для анализа общих элементов XML - PullRequest
7 голосов
/ 04 августа 2010

В настоящее время я использую SAX (Java) для анализа нескольких разных XML-документов, каждый из которых представляет разные данные и имеет немного разные структуры. По этой причине каждый XML-документ обрабатывается отдельным классом SAX (подкласс DefaultHandler).

Однако, есть некоторые структуры XML, которые могут появляться во всех этих различных документах. В идеале, я бы хотел сказать парсеру: "Эй, когда вы достигнете элемента complex_node, просто используйте ComplexNodeHandler, чтобы прочитать его, и верните мне результат. Если вы достигнете some_other_node, используйте OtherNodeHandler прочитать и вернуть мне этот результат ".

Однако я не вижу очевидного способа сделать это.

Должен ли я просто создать класс монолитного обработчика, который может читать все имеющиеся у меня документы (и устранять дублирование кода), или есть более разумный способ справиться с этим?

Ответы [ 2 ]

12 голосов
/ 04 августа 2010

Ниже приведен ответ на аналогичный вопрос ( Пропуск узлов с помощью 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, который проглотит все события для этого узла.выполнив проглатывание событий, он передает управление вашему основному 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 голосов
/ 04 августа 2010

У вас может быть один обработчик (ComplexNodeHandler), который обрабатывает только некоторые части документа (complex_node) и передает все остальные части другому обработчику. Конструктор для 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. Написание базового обработчика для общих частей и его наследование тоже может сработать, но я не уверен, что использование наследования здесь - хорошая идея.

...