Я в настоящее время использую SAX (Java) для парсинга горстки различные XML-документы с каждым документом, представляющим различные данные и имеющим немного отличающиеся структуры. Поэтому каждый XML-документ обрабатывается различным классом SAX (разделение на подклассы DefaultHandler
).
Однако существуют некоторые структуры XML, которые могут появиться во всех этих различных документах. Идеально, я хотел бы сказать синтаксический анализатор "Эй, когда Вы достигаете a complex_node
элемент, просто используйте ComplexNodeHandler
считать его и дать мне назад результат. Если Вы достигаете a some_other_node
, использовать OtherNodeHandler
считать его и дать мне назад тот результат".
Однако я не вижу очевидный способ сделать это.
Я должен просто просто сделать монолитный класс обработчика, который может прочитать все различные документы, которые я имею (и уничтожьте дублирование кода), или есть ли более умный способ обработать это?
Ниже я дал ответ на аналогичный вопрос ( Пропуск узлов с помощью саксофона ). Он демонстрирует, как поменять местами обработчики содержимого в 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 {
}
}
У вас может быть один обработчик (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. Написание базового обработчика для общих частей и его наследование тоже может сработать, но я не уверен, что использование наследования здесь - хорошая идея.