Используя SAX для парсинга общих элементов XML

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

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

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

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

7
задан Dave 4 August 2010 в 12:57
поделиться

2 ответа

Ниже я дал ответ на аналогичный вопрос ( Пропуск узлов с помощью саксофона ). Он демонстрирует, как поменять местами обработчики содержимого в 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 { 
    } 

} 
12
ответ дан 6 December 2019 в 21:08
поделиться

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

0
ответ дан 6 December 2019 в 21:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: