18c0693f

Введение в SAX

Characters()

Теперь, когда вы получили элемент, пойдем дальше и выберем его данные при помощи characters(). Взглянем на сигнатуру этого метода:
public void characters(char[] ch, int start, int length)
Заметьте, что нигде в методе нет информации о том, частью какого элемента являются эти символы. Если вам нужна такая информация, вы должны сохранять ее. В этом примере добавлены переменные для сохранения информации о текущем элементе и вопросе. (Тут также удалено много лишней информации, которая отображалась.)
Заметьте две важные вещи:
  • Размер: Событие characters() содержит больше, чем просто строку символов. Оно также содержит информацию о начале и о длине. На самом деле массив ch содержит полный документ. Приложение не должно пытаться читать символы за пределами размера, переданного в событие characters().
  • Частота: В спецификации SAX нет требований к процессору возвращать символы каким-то определенным способом, так что возможно, чтобы одна порция текста возвращалась в нескольких кусках. Всегда прежде, чем считать, что вы имеете все содержимое элемента, убедитесь, что произошло событие endElement(). Процессор может также использовать ignorableWhitespace(), чтобы возвращать пропуски внутри элемента.

  • Это всегда учитывается проверяющим парсером.
    ... public void printIndent(int indentSize) { for (int s = 0; s < indentSize; s++) { System.out.print(" "); } } String thisQuestion = ""; String thisElement = "";
    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { if (qName == "response") { System.out.println("User: " + atts.getValue("username")); } else if (qName == "question") { thisQuestion = atts.getValue("subject"); } thisElement = qName;
    } public void endElement(String namespaceURI, String localName, String qName) throws SAXException { thisQuestion = ""; thisElement = "";
    } public void characters(char[] ch, int start, int length) throws SAXException { if (thisElement == "question") { printIndent(4); System.out.print(thisQuestion + ": "); System.out.println(new String(ch, start, length)); } }
    ...
    Characters()


    Что такое SAX?

    Стандартным средством для чтения и манипулирования XML-файлами является Объектная Модель Документа, Document Object Model (DOM). К сожалению, этот метод, который включает в себя чтение всего файла и сохранение его в структуре дерева, может быть малоэффективным, медленным и требовать много ресурсов.
    Одной из альтернатив ему является Simple API for XML или SAX. SAX позволяет вам обрабатывать документ по мере его чтения, что устраняет необходимость ожидать с выполнением каких-то действий, пока весь документ не будет сохранен.
    SAX был разработан членами почтовой рассылки XML-DEV, а его Java-версия является сейчас проектом SourceForge (см. ). Целью проекта было обеспечение более естественного средства для работы с XML - другими словами, такого, которое не включает в себя таких расходов и концептуальных сложностей, каких требует DOM.
    Результатом был API, который является событийно-базированным. Парсер посылает события, такие, как начало и окончание элемента, в обработчик событий, который обрабатывает информацию. Затем с данными имеет дело само приложение. Исходный документ остается без изменений, но SAX обеспечивает средства для манипулирования данными, которые затем могут быть направлены в другой процесс или документ.
    SAX не имеет держателя стандарта, он не поддерживается консорциумом World Wide Web (W3C) или каким-то другим официальным держателем, но де-факто является стандартом в сообществе XML.


    DOM и обработка на базе дерева

    DOM является традиционным способом обработки XML-данных. При применении DOM данные загружаются в память в древовидную структуру.
    Например, тот же документ, который использовался как пример в , может быть представлен в виде узлов, показанных ниже:
    DOM и обработка на базе дерева
    Прямоугольники представляют элементные узлы, а овалы - текстовые узлы.
    DOM использует отношения предок-потомок. Например, в данном случае samples является корнем с пятью потомками: тремя текстовыми узлами (пропуски) и двумя элементными узлами, server и monitor.
    Важно представлять себе, что узлы server и monitor на самом деле имеют значения null. Вместо этого они содержат текстовые узлы (UNIX и color) в качестве потомков.


    EndDocument()

    И, конечно, когда документ полностью разобран, вы захотите напечатать результат окончательного подсчета, как показано ниже.
    Это также хорошее место для того, чтобы завершить все незавершенные концы, которые могли накопиться за время обработки.
    ... if (thisQuestion.equals("implant")) { implant = implant + new String(ch, start, length); } } } public int getInstances (String all, String choice) { ... return total; } public void endDocument() { System.out.println("Appearance of the aliens:"); System.out.println("A: " + getInstances(appearance, "A")); System.out.println("B: " + getInstances(appearance, "B")); ... }
    public static void main (String args[]) { ...
    EndDocument()


    EndElement()

    Может оказаться полезным замечать конец элемента. Например, это может быть сигналом к обработке содержимого элемента. Здесь вы будете использовать его для красивой печати документа с некоторыми отступами, показывающими уровень каждого элемента. Идея состоит в увеличении отступа, когда начинается новый элемент, и в уменьшении его, когда элемент заканчивается:
    ... int indent = 0;
    public void startDocument() throws SAXException { System.out.println("Tallying survey results..."); indent = -4;
    } public void printIndent(int indentSize) { for (int s = 0; s < indentSize; s++) { System.out.print(" "); } }
    public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { indent = indent + 4; printIndent(indent);
    System.out.print("Start element: "); System.out.println(qName); for (int att = 0; att < atts.getLength(); att++) { printIndent(indent + 4);
    String attName = atts.getLocalName(att); System.out.println(" " + attName + ": " + atts.getValue(attName)); } } public void endElement(String namespaceURI, String localName, String qName) throws SAXException { printIndent(indent); System.out.println("End Element: "+localName); indent = indent - 4; }
    ...
    EndElement()


    Файл примера

    Этот учебник демонстрирует конструирование приложения, которое использует SAX для обработки ответов группы пользователей, которых попросили заполнить анкету о случившимся с ними похищении их Пришельцами.
    Вот форма этой анкеты:
    Файл примера
    Ответы записаны в XML-файл:
    A B A D B C A A D A A C A D C


    IgnorableWhitespace()

    XML-документы, выработанные людьми (в отличие от выработанных программами) часто содержат пропуски, добавляемые для того, чтобы сделать документ более легким для чтения. Под пропусками подразумеваются переводы строк, символы табуляции и пробелы. В большинстве случаев пропуски являются лишними и должны игнорироваться при обработке данных.
    Все проверяющие парсеры н некоторые непроверяющие передают символы пропусков в обработчик содержимого не в событии characters(), а в событии ignorableWhitespace(). Это удобно, поскольку вы можете сосредоточиться только на реальных данных.
    Но что, если вам действительно нужны пропуски? В таком случает вы устанавливаете в элементе атрибут, который сигнализирует процессору о том, что не нужно игнорировать символы пропусков. Этот атрибут - xml:space, и он обычно предполагается default. (Это значит, что поведение процессора по умолчанию состоит в игнорировании пропусков.)
    Чтобы сообщить процессору, что не нужно игнорировать пропуски, установите его значение в preserve так:
    public void endElement( String namespaceURI, String localName, String qName) throws SAXException


    Инструменты

    Примеры в этом учебнике, если вы решите их выполнить, требуют, чтобы у вас были установлены и корректно работали следующие инструменты. (Выполнение примеров не является обязательным для понимания.)
  • Текстовый редактор: XML-файлы являются просто текстом. Все, что вам нужно для их создания - текстовый редактор.
  • Java 2 SDK, Standard Edition версии 1.4.x: Поддержка SAX встроена в последнюю версию Java (доступна на ), так что вам не нужно инсталлировать какие-либо дополнительные классы. Если вы используете более раннюю версию Java, например, Java 1.3.x, вам еще нужен XML-парсер, такой как Xerces-Java из проекта Apache (доступен на ), или Java API for XML Parsing (JAXP) фирмы Sun, часть Java Web Services Developer Pack (доступен на ). Вы можете также выгрузить официальную версию из SourceForge (доступна на ).
  • Другие языки: Если вы захотите адаптировать эти примеры, реализации SAX также доступны в других языках программирования. Вы можете найти информацию о реализациях SAX-парсера для C, C++, Visual Basic, Perl и Python на .




  • Использование XMLFilter для преобразования данных

    XMLFilters может также использоваться для быстрого и легкого преобразования данных при помощи XSLT.
    Само преобразование лежит вне сферы рассмотрения нашего учебника, но вот краткий взгляд на то, как вы можете применить его:
    import javax.xml.transform.stream.StreamSource; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.sax.SAXTransformerFactory; import org.xml.sax.XMLFilter;
    ... public static void main (String args[]) { XMLReader xmlReader = null; try { SAXParserFactory spfactory = SAXParserFactory.newInstance(); spfactory.setValidating(false); SAXParser saxParser = spfactory.newSAXParser(); xmlReader = saxParser.getXMLReader(); TransformerFactory tFactory = TransformerFactory.newInstance(); SAXTransformerFactory saxTFactory = ((SAXTransformerFactory) tFactory); XMLFilter xmlFilter = saxTFactory.newXMLFilter(new StreamSource("surveys.xsl"));
    xmlFilter.setParent(xmlReader); Serializer serializer = SerializerFactory.getSerializer( OutputProperties.getDefaultMethodProperties("xml")); serializer.setOutputStream(System.out);
    xmlFilter.setContentHandler( serializer.asContentHandler() );
    InputSource source = new InputSource("surveys.xml"); xmlFilter.parse(source); } catch (Exception e) { System.err.println(e); System.exit(1); } } ...
    Сначала вам нужно создать фильтр - но вместо создания его с нуля, создайте фильтр, который специально предназначен для преобразования на основе таблицы стилей.
    Затем, так же, как вы делали, когда выводили непосредственно в файл, создайте Serializer для вывода результата преобразования.
    В основном фильтр выполняет преобразование, затем обрабатывает события на XMLReader. Однако, в конечном счете приемником являетсяSerializer.


    Как работает обработка в SAX

    SAX анализирует поток XML и проходит через, как по телетайпной ленте. Рассмотрим следующий XML-код:
    UNIX color
    Процессор SAX, анализирующий этот код, будет генерировать, как правило, следующие события:
    Начало документа Начало элемента (samples) Символы (пропуск) Начало элемента (server) Символы (UNIX) Конец элемента (server) Символы (пропуск) Начало элемента (monitor) Символы (color) Конец элемента (monitor) Символы (пропуск) Конец элемента (samples)
    SAX API дает возможность разработчику выловить эти события и работать по ним.
    Обработка в SAX включает в себя следующие шаги:
  • Создание обработчика событий.
  • Создание парсера SAX.
  • Назначение обработчика событий для парсера.
  • Разбор документа с посылкой каждого события в обработчик.



  • Как выбрать между SAX и DOM

    Использовать вам DOM или SAX, зависит от нескольких факторов:
  • Назначение приложения: Если вы собираетесь делать изменения в данных и выводить их как XML, то в большинстве случаев способом для этого является DOM. Нельзя сказать, что вы не можете делать изменения при использовании SAX, но этот процесс значительно более сложен, так как вы должны делать изменения в копии данных, а не в самих данных.
  • Объем данных: Для больших файлов SAX является лучшим выбором.
  • Как будут использованы данные: Если на самом деле будет использована только небольшая часть данных, вам, возможно, лучше применить SAX, чтобы выделить ее в ваше приложение. С другой стороны, если вы знаете, что вам придется ссылаться назад в большом объеме информации, которая уже была обработана, SAX, возможно, не является правильным выбором.
  • Требования к быстродействию: реализации SAX обычно быстрее, чем реализации DOM.

  • Важно помнить, что SAX и DOM не являются взаимоисключающими. Вы можете использовать DOM для создания потока событий SAX, и вы можете использовать SAX для создания дерева DOM. Фактически, большинство парсеров, применяемых для создания дерева DOM, используют SAX, чтобы сделать это!


    Непосредственное задание драйвера SAX

    Если есть обработчик событий, то следующим шагом является создание парсера, XMLReader, при помощи драйвера SAX. Вы можете создать парсер одним из трех способов:
  • Непосредственно вызвать драйвер
  • Разрешить указание драйвера во время выполнения
  • Передать драйвер как аргумент для createXMLReader()

  • Если вы знаете имя класса драйвера SAX, вы можете вызвать драйвер непосредственно. Например, если это класс (на самом деле не существующий) com.nc.xml.SAXDriver, вы можете применить такой код:
    try { XMLReader xmlReader = new com.nc.xml.SAXDriver();
    } catch (Exception e) { System.out.println("Can't create the parser: " + e.getMessage()); }
    чтобы непосредственно создать XMLReader.
    Вы можете также использовать системные свойства, чтобы сделать свое приложение более гибким. Например, вы можете задать имя класса как значение свойства org.xml.sax.driver из командной строки, когда вы запускаете приложение:
    java -Dorg.xml.sax.driver=com.nc.xml.SAXDriver SurveyReader
    (Заметьте, опция -D не допускает пробела после нее.)
    Это делает информацию доступной для класса XMLReaderFactory, так что вы можете сказать:
    try { XMLReader xmlReader = XMLReaderFactory.createXMLReader();
    } catch (Exception e) { System.out.println("Can't create the parser: " + e.getMessage()); }
    Если вы знаете имя драйвера, вы можете также передать его непосредственно как аргумент для createXMLReader().


    Нужен ли мне этот учебник?

    В этом учебнике исследуется Simple API for XML версии 2.0.x или SAX 2.0.x.
    Он предназначен для разработчиков, которые понимают XML и хотят изучить этот легкий событийно-базированный API для работы с XML-данными. Предполагается, что вы знакомы с такими концепциями, как правильное форматирование и с теговой природой XML-документа. (Вы можете получит базовые представления о самом XML из учебника .) В этом учебнике вы узнаете, как использовать SAX, чтобы выбирать, манипулировать и выводить XML-данные.
    Предварительные замечания: SAX доступен во многих языках программирования, таких, как Java, Perl, C++ и Python. Этот учебник использует для демонстраций язык Java, но концепции в разных языках, по существу, одинаковы, и вы можете получить понимание SAX, даже не работая на самом деле с этими примерами.


    Об авторе

    Nicholas Chase, автор , участвовал в разработке Web-сайтов для таких компаний, как Lucent Technologies, Sun Microsystems, Oracle и Tampa Bay Buccaneers. Nick был преподавателем физики в высшей школе, менеджером низшего звена по использованию радиоактивных отходов, редактором онлайнового журнала научной фантастики, инженером по мультимедиа и инструктором по Oracle. В последнее время он - руководитель технического отдела фирмы Site Dynamics Interactive Communications в Clearwater, Florida, USA и автор четырех книг по Web-разработке, включая (Sams). Он любит слышать отзывы читателей, с ним можно связаться по адресу nicholas@nicholaschase.com.

    Обучение

  • Для изучения основ XML прочитайте учебник "" на (developerWorks, август 2002). Перевод на нашем сайте - ""
  • Посмотрите официальную страницу (http://www.saxproject.org).
  • Прочитайте об использовании (developerWorks, март 2003).
  • Узнайте, как строить (developerWorks, март 2003).
  • Изучите (developerWorks, июль 2003).
  • Изучите подробнее (developerWorks, июнь 2001).
  • Поймите, как (developerWorks, июнь 2002).
  • Рассмотрите (developerWorks, июль 2002).
  • Узнайте, как превратить поток SAX в объект DOM или JDOM в (developerWorks, апрель 2001).
  • Закажите книгу , Nicholas Chase, автора этого учебника (http://www.amazon.com/exec/obidos/ASIN/0672324229/ref=nosim/thevanguardsc-20).
  • SAX был разработан участниками почтовой рассылки XML-DEV. Возьмите Java-версию, сейчас она в проекте (http://sourceforge.net/project/showfiles.php?group_id=29449).
  • Возьмите реализации SAX, которые имеются для (http://www.saxproject.org/?selected=langs).
  • Узнайте, как вы можете стать (http://www-1.ibm.com/certify/certs/adcdxmlrt.shtml).



  • Определение пространств имен

    Для данных могут быть определены также и другие пространства имен. Например, созданием пространства имен revised вы можете добавить второй набор данных - скажем, о постгипнотическом состоянии, - не беспокоясь об имеющихся данных.
    Пространство имен вместе с алиасом создаются обычно (но не обязательно) в корневом элементе документа. Этот алиас используется как префикс для элементов и атрибутов - при необходимости, если используется более одного пространства имен, - чтобы задать правильное пространство имен.
    Рассмотрим код, приведенный ниже:
    A B A D B D A A D A
    ...
    Пространство имен и алиас, revised, использованы для создания дополнительного элемента question.
    Помните, что revised - не пространство имен, а алиас! Настоящее пространство имен - http://www.nicholaschase.com/surveys/revised/.