Язык XML. Использование Java XML-обработчиков
Internet Explorer, несмотря на мощную встроенную поддержку XML, сегодня далеко не единственное средство, которое можно использовать для работы с новым языком. Обработкой XML документа на стороне клиента или сервера может также заниматься любой другой анализатор XML-документов, который конвертирует их в обычную HTML страницу или извлекает из нее информацию для других приложений.
Что такое XML Parser?
Любой XML-процессор, являясь, по сути, транслятором языка разметки, может быть разбит на несколько модулей, отвечающих за лексический, синтаксический и семантический анализ содержимого документа. Понятно, что если бы мы были вынуждены каждый раз писать все эти блоки самостоятельно, необходимость в XML как в таковом бы отпала - основное его преимущество, как уже упоминалось ранее, заключается в стандартном способе извлечения информации из документа. Синтаксически правильно составленный XML-документ может быть разобран любым универсальным XML анализатором, и нашему XML-обработчику остается лишь использовать полученные на его выходе "чистые" данные (прошедшие синтаксический анализ) - интерпретировать содержимое документа, в соответствии с его DTD-описанием или схемами данных.
Конечно, синтаксический анализатор может быть довольно легко реализован и самостоятельно, например, в Perl, с его мощными возможностями обработки регулярных выражений. Но в общем случае такой "ручной" способ является довольно нетривиальной задачей, требующей некоторых усилий и является дополнительным источником ошибок. Поэтому применение универсальных XML-анализаторов может существенно облегчить жизнь разработчикам, тем более, что уже сегодня количество свободно доступных программ такого рода довольно велико.
В функции современного XML-процессора обычно входит получение общих сведений о документе, извлечение информации о его структуре и построения некоторой абстрактной объектной модели данных, представляющей эту структуру. По способу проверки разбираемых документов универсальные программы-анализаторы делятся на два типа: верифицирующие, способные обнаружить DTD-описания грамматики языка и использовать их для проверки документа на семантическую корректность; и неверифицирующие, не осуществляющие такой проверки.
Описывая разобранный XML-документ, универсальная программа-анализатор должна представить его структуру в виде упорядоченной модели данных, для доступа к которой используется какая-то станадртная, описанная в соответствующей спецификации библиотека классов - интерфейсов XML документа. На сегодняшний день существует два подхода к их построению: собыйтийный - Simple API for XML, SAX и объектно-ориентированный - DOM(Document Object Model). Рассмотрим их использование на конкретных примерах.
Что такое SAX
Сегодня стандартным интерфейсом для большинства универсальных XML-анализаторов является событийно-ориентированное API SAX - Simple API for XML.
Термин событийно-ориентированный является ключевым в этом определении и объясняет способ использования SAX. Каждый раз, когда при разборе XML документа анализатор оказывается в каком-то новом состоянии - обнаруживает какую-либо синтаксическую конструкцию XML-документа (элемент, символ, шаблон, и т.д.), фиксирует начало, конец объявлений элементов документа, просматривает DTD-правила или находит ошибку, он воспринимает его как произошедшее событие и вызывает внешнюю процедуру - обработчик этого события. Информация о содержимом текущей конструкции документа передается ему в качестве параметров функции. Обработчик события - это какой-то объект приложения, который выполняет необходимые для обработки полученной из XML информации действия и осуществляет таким образом непосредственный разбор содержимого. После завершения этой функции управление опять передается XML-анализатору и процесс разбора продолжается.
Реализацией этого механизма в Java SAX 1.0 является библиотека классов org.xml.sax (их можно получить, например, с узла: http://www.megginson.com/, но обычно эти классы включаются в состав XML -анализатора). Наследуя клссы SAX-совместимого анализатора, мы получаем универсальный доступ к XML документу при помощи классов, содержимое и механизм использование которых приведено в соответствующем описании.
Последовательный разбор XML-документа SAX-обработчиком обычно производится по следующей схеме (более подробное описание приведено ниже):
- загрузить документ, установить обработчики событий, начать просмотр его содержимого (если есть DTD-описания, то - их разбор);
- найдено начало документа (его корневой, самый первый элемент) - вызвать виртуальную функцию- обработчик события startDocument;
- каждый раз, когда при разборе будет найден открывающий тэг элемента вызывается обработчик-функция startElement. В качестве параметров ей передаются название элемента и список его атрибутов;
- найдено содержимое элемента - передать его соответствующему обработчику - characters, ignorableWhitespace,processingInstruction и т.д.;
- если внутри текущего элемента есть подэлементы, то эта процедура повторяется;
- найден закрывающий тэг элемента - обработать событие endElement();
- найден закрывающий тэг корневого элемента -обработать событие endDocument;
- если в процессе обработки были обнаружены ошибки, то анализатором вызываются обработчики предупреждений (warning), ошибок (error) и критических ошибок обработчика (fatalError).
Ссылка на объект класса обработчика событий может передаваться объекту XML-анализатора при помощи следующих функций:
parser.setDocumentHandler(event_class); // - обработчик событий документа
parser.setEntityResolver(event_class); // - обработчик событий загрузки DTD-описаний
parser.setDTDHandler(event_class); // - обработчик событий при анализе DTD-описаний
parser.setErrorHandler(event_class); // - обработчик чрезвычайных ситуаций
Здесь event_class - объект созданного нами ранее класса.
Краткое описание некоторых из объектов-обработчиков событий приведено в следующей таблице:
Объект DocumentHandler
startDocument() |
Начало документа |
endDocument() |
Конец документа |
startElement (String name, AttributeList atts) |
Начало элемента. Функции передается название элемента(открывающий тэг) и список его атрибутов. |
endElement (String name) |
Конец элемента |
characters (char[] cbuf, int start, int len) |
Обработка массива текстовых символов |
ignorableWhitespace (char[] cbuf, int start, int len) |
Необрабатываемые символы |
processingInstruction (String target, String data) |
Обработка инструкций XML-анализатора) |
Объект ErrorHandler
warning (SAXParseException e) |
Получение сообщения о "несерьезной" ошибке. Пдробная информация содержится в передаваемом объекте класса SA XParseException |
error (SAXParseException e) |
Сообщение об ошибке |
fatalError (SAXParseException e) |
Сообщение о критической ошибке |
Для демонстрции использования этих методов рассмотрим небольшой пример обработчика регистрационного XML-документа (его структура описана в примере 2 первого раздела статьи). Java-приложение выводит содержимое документа и информацию о его структуре, путь к документу задается в командной строке. Для компилирования потребуется JDK 1.1.4 и классы SAX, находящиеся либо в текущем пакете, либо вместе с другими классами в classes.zip.
import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import com.ibm.xml.parsers.DOMParser; import org.xml.sax.Parser; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.AttributeList; import org.xml.sax.HandlerBase; import org.xml.sax.helpers.ParserFactory; class saxParser extends HandlerBase{ private PrintWriter out; private int elements; private int attributes; private int characters; private int ignorableWhitespace; private String url; public saxParser(String url_str) { url = url_str; try { out = new PrintWriter (new OutputStreamWriter(System.out, "koi8-r")); } catch (UnsupportedEncodingException e) { } } //=============================================== // Обработчики событий. Методы интерфейса DocumentHandler //======================== // Начало документа public void startDocument() { // Статистика elements = 0; attributes = 0; characters = 0; ignorableWhitespace = 0; // Процессорные инструкции out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); } // Конец документа public void endDocument() { out.flush(); } // Встретился открывающий тэг элемента // public void startElement(String name, AttributeList attrs) { elements++; if (attrs != null) { attributes += attrs.getLength(); } // Печать тэга элемента вместе со списком его атрибутов, например, <elem id="48"> out.print('<'); out.print(name); if (attrs != null) { int len = attrs.getLength(); for (int i = 0; i < len; i++) { out.print(' '); out.print(attrs.getName(i)); out.print("=\""); out.print(attrs.getValue(i)); out.print('"'); } } out.println('>'); } // Встретился закрывающий тэг элемента public void endElement(String name) { out.println("</"+name+">"); } // Текстовые символы public void characters(char ch[], int start, int length) { characters += length; out.println(new String(ch, start, length)); } // Необрабатываемые символы(например, содержимое секции CDATA) public void ignorableWhitespace(char ch[], int start, int length) { characters(ch, start, length); } // Инструкции XML-процессору public void processingInstruction (String target, String data) { out.print("<?"); out.print(target); if (data != null && data.length() > 0) { out.print(' '); out.print(data); } out.print("?>"); } //=================================================== // Методы интерфейса ErrorHandler //=============================== // Последнее предупреждение public void warning(SAXParseException ex) { System.err.println("Warning at "+ ex.getLineNumber()+" . "+ ex.getColumnNumber()+" - "+ ex.getMessage()); } // Произошла ошибка public void error(SAXParseException ex) { System.err.println("Error at {"+ ex.getLineNumber()+" . "+ ex.getColumnNumber()+" - "+ ex.getMessage()); } // Такие ошибки исправить уже нельзя public void fatalError(SAXParseException ex) throws SAXException { System.err.println("Fatal error at {"+ ex.getLineNumber()+" . "+ ex.getColumnNumber()+" - "+ ex.getMessage()); throw ex; } //============================================ // Вывести информацию о документе //=============================== public void printInfo() { System.out.println(); System.out.println("Документ "+url+" был успешно обработан"); System.out.println("Элементов : "+elements); System.out.println("Атрибутов : "+attributes); System.out.println("Символов : "+characters); } } //================================================ // Обработка XML документа //======================== public class saxSample{ public static void main(String argv[]) { try { saxParser sample = new saxParser(argv[0]); Parser parser = ParserFactory.makeParser ("com.ibm.xml.parsers.SAXParser"); parser.setDocumentHandler(sample); parser.setErrorHandler(sample); parser.parse(argv[0]); sample.printInfo(); } catch (Exception e) { e.printStackTrace(System.err); } } }
Комментарии
Первым шагом в процессе построения XML-обработчика является создание объекта из класса анализатора (в нашем случае это классы из паекета com.ibm.xml.parsers). Для этого можно использовать класс ParserFactory, входящий в org.xml.sax.helpers:
import org.xml.sax.*; ... Parser parser = ParseFactory.createParser(); ...
Затем следует определить обработчики
возникающих в процессе разбора XML-документа событий. Приложению необязательно
устанавливать все обработчики сразу - в классе HandlerBase все события могут
обрабатываться "по умолчанию". Более подробную информацию по использованию
SAX-анализаторов можно найти в примерах приложений в пакетах анализатора или на
сервере www.megginson.com.