| Каталог | Индекс раздела |
© IBM deweloperWork
© А.С.Деревянко (перевод)
В этом учебнике исследуется Simple API for XML версии 2.0.x или SAX 2.0.x.
Он предназначен для разработчиков, которые понимают XML и хотят изучить этот легкий событийно-базированный API для работы с XML-данными. Предполагается, что вы знакомы с такими концепциями, как правильное форматирование и с теговой природой XML-документа. (Вы можете получит базовые представления о самом XML из учебника Введение в XML.) В этом учебнике вы узнаете, как использовать SAX, чтобы выбирать, манипулировать и выводить XML-данные.
Предварительные замечания: SAX доступен во многих языках программирования, таких, как Java, Perl, C++ и Python. Этот учебник использует для демонстраций язык Java, но концепции в разных языках, по существу, одинаковы, и вы можете получить понимание 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.
Примеры в этом учебнике, если вы решите их выполнить, требуют, чтобы у вас были установлены и корректно работали следующие инструменты. (Выполнение примеров не является обязательным для понимания.)
SAX анализирует поток XML и проходит через, как по телетайпной ленте. Рассмотрим следующий XML-код:
Процессор SAX, анализирующий этот код, будет генерировать, как правило, следующие события:
SAX API дает возможность разработчику выловить эти события и работать по ним.
Обработка в SAX включает в себя следующие шаги:
Преимущества этого вида обработки очень похожи на преимущества потоковых носителей. Анализ может начинаться немедленно, а не ожидать всех данных для обработки. Также поскольку работает с данными по мере их поступления, не требуется сохранять их в памяти. Это огромное преимущество, когда поступают большие документы. Фактически, приложение даже не должно разбирать весь документ; оно может остановиться, когда будут удовлетворены некоторые критерии. В общем случае SAX также значительно быстрее, чем его альтернатива, DOM.
С другой стороны, поскольку приложение не сохраняет каким-либо образом данные, невозможно при помощи SAX делать в них изменения или возвращаться назад по потоку данных.
DOM является традиционным способом обработки XML-данных. При применении DOM данные загружаются в память в древовидную структуру.
Например, тот же документ, который использовался как пример в Как работает обработка в SAX, может быть представлен в виде узлов, показанных ниже:
Прямоугольники представляют элементные узлы, а овалы - текстовые узлы.
DOM использует отношения предок-потомок. Например, в данном случае Важно представлять себе, что узлы DOM и вообще обработка на базе дерева имеет несколько преимуществ. Во-первых, поскольку дерево сохраняется в памяти, оно может быть модифицировано, так что приложение может делать изменения в данных и в структуре. Оно также может в сколько угодно двигаться вверх и вниз по дереву, в отличие от одного прохода SAX. DOM также может быть гораздо проще в применении.
С другой стороны, построение таких деревьев в памяти включает в себя много накладных расходов. Не так уж необычно, если большие файлы полностью перекрывают возможности системы. Кроме того, создание дерева DOM может быть очень медленным процессом.
Использовать вам DOM или SAX, зависит от нескольких факторов:
Важно помнить, что SAX и DOM не являются взаимоисключающими. Вы можете использовать DOM для создания потока событий SAX, и вы можете использовать SAX для создания дерева DOM. Фактически, большинство парсеров, применяемых для создания дерева DOM, используют SAX, чтобы сделать это!
Этот учебник демонстрирует конструирование приложения, которое использует SAX для обработки ответов группы пользователей, которых попросили заполнить анкету о случившимся с ними похищении их Пришельцами.
Вот форма этой анкеты:
Ответы записаны в XML-файл:
Прежде, чем приложение сможет применять SAX для обработки XML-документа, оно должно создать обработчик событий. SAX предоставляет класс, Правильный разбор при помощи SAX требует специфических вызовов методов обработчика, и эти вызовы не могут быть статическими. Это значит, что вы должны специально создать экземпляр объекта обработчика, так что я сделаю краткий обзор того, как это делается.
Когда создается новый объект, ищется конструктор класса для его выполнения. Например:
Когда выполняется метод Если есть обработчик событий, то следующим шагом является создание парсера, Если вы знаете имя класса драйвера SAX, вы можете вызвать драйвер непосредственно. Например, если это класс (на самом деле не существующий) чтобы непосредственно создать Вы можете также использовать системные свойства, чтобы сделать свое приложение более гибким. Например, вы можете задать имя класса как значение свойства (Заметьте, опция Это делает информацию доступной для класса Если вы знаете имя драйвера, вы можете также передать его непосредственно как аргумент для Этот пример использует пару классов, Сначала объявите Чтобы что-либо делать с XML-документом, вы должны прочитать информацию в нем. Приложение, которое это делает, называется парсером.
Есть два вида парсеров: проверяющие и непроверяющие.
Документы, которые должны проверяться проверяющим парсером, называются правильными документами.
В этом учебнике вы не проверяете результаты анкеты, так что отключите проверку правильности для любого парсера, создаваемого Парсер должен посылать свои события в Это, конечно, не единственный вариант обработчика содержимого. Позже в нашем учебнике вы увидите другие варианты, когда в учебнике будет исследоваться Сериализация потока SAX.
Чтобы на самом деле разобрать файл (или что-то еще в этом роде), вам нужен Теперь вы готовы к тому, чтобы разобрать файл. Метод Вы можете откомпилировать и выполнить программу, но на этом этапе ничего не будет происходить, поскольку приложение еще не имеет никаких определенных событий.
На данный момент ничего не должно случиться, но, конечно, всегда есть вероятность возникновения проблем с данными, которые вы пытаетесь разобрать. В такой ситуации будет полезным иметь наряду с обработчиком содержимого и обработчик ошибок.
Вы можете создать обработчик ошибок точно так же, как вы создавали обработчик содержимого. Обычно вы создаете его как отдельный экземпляр Установите новый Если вы запустите приложение, все еще ничего не будет происходить, поскольку реализации по умолчанию для событий не делают ничего. В следующем разделе мы рассмотрим добавление новых реализаций для обработки событий, происходящих во время разбора.
Теперь начнем рассмотрение действительно данных. Для каждого элемента пример возвращает имя, которое передается в событие На самом деле парсер передает несколько частей информации для каждого элемента:
Начнем с перечисления имен всех элементов:
Событие Вы можете выбрать значение атрибута по его позиции в массиве или по имени атрибута:
Может оказаться полезным замечать конец элемента. Например, это может быть сигналом к обработке содержимого элемента. Здесь вы будете использовать его для красивой печати документа с некоторыми отступами, показывающими уровень каждого элемента. Идея состоит в увеличении отступа, когда начинается новый элемент, и в уменьшении его, когда элемент заканчивается:
Теперь, когда вы получили элемент, пойдем дальше и выберем его данные при помощи Заметьте, что нигде в методе нет информации о том, частью какого элемента являются эти символы. Если вам нужна такая информация, вы должны сохранять ее. В этом примере добавлены переменные для сохранения информации о текущем элементе и вопросе. (Тут также удалено много лишней информации, которая отображалась.)
Заметьте две важные вещи:
Это всегда учитывается проверяющим парсером.
Теперь, когда вы получили данные, пойдем дальше и произведем подсчет.
Вот пример того, как строить строки для анализа, когда опрос закончен.
Важно помнить, что единственной причиной, по которой вы можете выполнить это в методе XML-документы, выработанные людьми (в отличие от выработанных программами) часто содержат пропуски, добавляемые для того, чтобы сделать документ более легким для чтения. Под пропусками подразумеваются переводы строк, символы табуляции и пробелы. В большинстве случаев пропуски являются лишними и должны игнорироваться при обработке данных.
Все проверяющие парсеры н некоторые непроверяющие передают символы пропусков в обработчик содержимого не в событии Но что, если вам действительно нужны пропуски? В таком случает вы устанавливаете в элементе атрибут, который сигнализирует процессору о том, что не нужно игнорировать символы пропусков. Этот атрибут - Чтобы сообщить процессору, что не нужно игнорировать пропуски, установите его значение в И, конечно, когда документ полностью разобран, вы захотите напечатать результат окончательного подсчета, как показано ниже.
Это также хорошее место для того, чтобы завершить все незавершенные концы, которые могли накопиться за время обработки.
Все это хорошо, но иногда вы можете захотеть включить информацию непосредственно для приложения, которое обрабатывает данные. Одним их примеров этого является случай, когда вы хотите обрабатывать документ при помощи таблицы стилей, как, например:
Другие приложения могут получать информацию таким же путем. Например, опрос может иметь статистический пример, который, естественно, недостаточно велик, чтобы рассматривать его серьезно. Вы можете добавить инструкцию обработки, только для вашего приложения, которая задает коэффициент для умножения ответов:
Эта запись будет подхвачена событием Так же, как Вам нужно побеспокоиться о трех событиях: Одно из главных усовершенствований, добавленных в SAX версии 2.0 явилось добавление поддержки пространств имен, которая позволила разработчикам использовать информацию из разных источников или для разных целей без конфликтов. В производственной среде часто происходит так, что данные в потоке SAX приходят из многих разных источников.
Пространства имен концептуально являются зонами, в которых имена должны быть уникальными.
Например, я работаю в офисе и у меня то же имя, что и у клиента. Если я где-то в офисе, и секретарь говорит: "Ник, возьми трубку на 1-й линии", - каждый понимает, что она имеет в виду меня, потому что я нахожусь в "пространстве имен офиса". Аналогично, если она говорит "Ник звонит по 1-й линии", - каждый знает, что она говорит о клиенте, потому что звонящий находится вне пространства имен офиса.
С другой стороны, если я нахожусь вне офиса, и она делает такое же заявление, возможно недоразумение, поскольку существуют две возможности.
Те же проблемы возникают, когда XML-данные из разных источников комбинируются (как в информации о кредитоспособности в файле-примере, подробно рассматриваемом в этом учебнике позже).
Поскольку идентификаторы для пространств имен должны быть уникальными, они обозначаются при помощи Унифицированных Идентификаторов Ресурсов, Uniform Resource Identifiers или URI. Например, пространство имен по умолчанию для данных примера будет обозначено при помощи атрибута Любые элементы, для которых не указано пространство имен, находятся в пространстве имен по умолчанию, Важно отметить громадную разницу между пространством имен по умолчанию и отсутствием пространства имен вообще. В этом случает элементы, которые не имеют префикса пространства имен, находятся в пространстве имен по умолчанию. Если же не существует пространства имен по умолчанию, такие элементы находятся вне пространства имен. Это различие становится существенным, когда вы имеете дело с Пространством имен на атрибутах.
Вы можете также создавать вторичные пространства имен и добавлять в них элементы или атрибуты.
Для данных могут быть определены также и другие пространства имен. Например, созданием пространства имен Пространство имен вместе с алиасом создаются обычно (но не обязательно) в корневом элементе документа. Этот алиас используется как префикс для элементов и атрибутов - при необходимости, если используется более одного пространства имен, - чтобы задать правильное пространство имен.
Рассмотрим код, приведенный ниже:
Пространство имен и алиас, Помните, что Версия SAX 2.0 добавляет функциональность распознавания разных пространств имен, как было вкратце отмечено а startElement().
Вы можете использовать эти новые возможности несколькими способами, но начнем с того, что убедимся, что только исходные ответы представляются в результатах. Иначе ответ Боба может быть засчитан дважды.
Поскольку ответы не будут учитываться, пока Заметьте, что приложение рассматривает URI пространства имен (или в данном случае URL), а не алиас.
Атрибуты тоже могут принадлежать к определенному пространству имен. Например, если имя вопроса изменяется второй раз, вы можете добавить второй относящийся к нему атрибут, Несколько странно, но атрибуты никогда не находятся в пространстве имен по умолчанию. Даже если Even было объявлено пространство имен по умолчанию, атрибут без префикса рассматривается как вообще не имеющий пространства имен. Это означает, что Список Способ, которым парсер SAX обрабатывает локальные имена и квалифицированные имена, может быть немного странным. Например, парсер Java по умолчанию не будет сообщать о значениях локальных имен, если специально не включена обработка пространств имен:
Если у вас возникают трудности с получением информации, попробуйте включить это свойство парсера.
Приведенные выше примеры рассматривали основы работы с данными, но это только поверхностный взгляд на то, сто может делать SAX. Данные могут быть направлены в другой процессор SAX, на преобразование или (конечно же) в файл. В этом разделе я покажу вам некоторые из этих вариантов.
Вывод потока событий SAX в файл называется его сериализацией. Вы можете написать файл сами, но гораздо легче просто объект Создайте объект Вы можете затем установить Поскольку SAX включает в себя анализ данных (а не только сохранение их) по мере их передачи, вам может показаться, что не существует способа изменить данные перед их анализом.
Эту проблему решают В основном это работает таким образом:
Теперь вы хотите создать фильтр, который уничтожает исходные ответы и вместо них использует исправленные ответы. Чтобы сделать это, вам нужно стереть оригиналы, а затем изменить пространство имен на исправленные ответы, так что Это реализовано путем создания нового класса, который расширяет Взгляните, что происходит здесь. Когда происходит событие Эти измененные данные передаются в Теперь пришло время использовать фильтр. Первым делом нужно создать новый экземпляр, а затем назначить его предком исходный Затем установите обработчики содержимого и ошибок фильтра, отличные от обработчиков Наконец, используйте фильтр для разбора файла вместо Поскольку Глубина вложения для такой возможности не ограничена. Теоретически, вы можете создавать сколь угодно длинную цепочку фильтров, каждый из которых вызывает следующий.
XMLFilters может также использоваться для быстрого и легкого преобразования данных при помощи XSLT.
Само преобразование лежит вне сферы рассмотрения нашего учебника, но вот краткий взгляд на то, как вы можете применить его:
Сначала вам нужно создать фильтр - но вместо создания его с нуля, создайте фильтр, который специально предназначен для преобразования на основе таблицы стилей.
Затем, так же, как вы делали, когда выводили непосредственно в файл, создайте В основном фильтр выполняет преобразование, затем обрабатывает события на SAX является более быстрым, более легким способом чтения и манипулирования XML-данными, чем DOM. SAX является событийно-базированным процессором, который позволяет вам иметь дело с элементами, атрибутами и другими данными по мере их появления в исходном документе. Из-за своей архитектуры SAX - система преимущественно только для чтения, но это не мешает вам использовать данные. Этот учебник также рассматривает способы использования SAX для определения пространств имен и вывода и преобразования данных при помощи XSL. И наконец, вы рассмотрели фильтры, которые позволяют вам связывать операции в цепочки.
Nicholas Chase, автор Studio B, участвовал в разработке Web-сайтов для таких компаний, как Lucent Technologies, Sun Microsystems, Oracle и Tampa Bay Buccaneers. Nick был преподавателем физики в высшей школе, менеджером низшего звена по использованию радиоактивных отходов, редактором онлайнового журнала научной фантастики, инженером по мультимедиа и инструктором по Oracle. В последнее время он - руководитель технического отдела фирмы Site Dynamics Interactive Communications в Clearwater, Florida, USA и автор четырех книг по Web-разработке, включая XML Primer Plus (Sams). Он любит слышать отзывы читателей, с ним можно связаться по адресу nicholas@nicholaschase.com.
Что такое SAX?
Инструменты
Раздел 2. DOM, SAX и, когда каждый из них применяется
Как работает обработка в SAX
<?xml version="1.0"?>
<samples>
<server>UNIX</server>
<monitor>color</monitor>
</samples>
Начало документа
Начало элемента (samples)
Символы (пропуск)
Начало элемента (server)
Символы (UNIX)
Конец элемента (server)
Символы (пропуск)
Начало элемента (monitor)
Символы (color)
Конец элемента (monitor)
Символы (пропуск)
Конец элемента (samples)
За и против событийно-базированной обработки
DOM и обработка на базе дерева
samples является корнем с пятью потомками: тремя текстовыми узлами (пропуски) и двумя элементными узлами, server и monitor.
server и monitor на самом деле имеют значения null. Вместо этого они содержат текстовые узлы (UNIX и color) в качестве потомков.
За и против обработки на базе дерева
Как выбрать между SAX и DOM
Раздел 3. Создание парсера SAX
Файл примера
<?xml version="1.0"?>
<surveys>
<response username="bob">
<question subject="appearance">A</question>
<question subject="communication">B</question>
<question subject="ship">A</question>
<question subject="inside">D</question>
<question subject="implant">B</question>
</response>
<response username="sue">
<question subject="appearance">C</question>
<question subject="communication">A</question>
<question subject="ship">A</question>
<question subject="inside">D</question>
<question subject="implant">A</question>
</response>
<response username="carol">
<question subject="appearance">A</question>
<question subject="communication">C</question>
<question subject="ship">A</question>
<question subject="inside">D</question>
<question subject="implant">C</question>
</response>
</surveys>
Создание обработчика событий
DefaultHandler, который приложение может расширять.
import org.xml.sax.helpers.DefaultHandler;
public class SurveyReader extends DefaultHandler
{
public SurveyReader() {
System.out.println("Object Created.");
}
public void showEvent(String name) {
System.out.println("Hello, "+name+"!");
}
public static void main (String args[]) {
SurveyReader reader = new SurveyReader();
reader.showEvent("Nick");
}
}
main, он создает новый экземпляр класса SurveyReader. Это приводит к выполнению конструктора и выводу Object Created (вместе с приветствием ниже). Вы можете затем использовать этот объект, чтобы выполнять метод showEvent().
Непосредственное задание драйвера SAX
XMLReader, при помощи драйвера SAX. Вы можете создать парсер одним из трех способов:
createXMLReader()
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().
Создание парсера
SAXParserFactory и SAXParser, чтобы создать парсер, так что вам не нужно знать имя самого драйвера.
xmlReader типа XMLReader, а затем используйте SAXParserFactory для создания SAXParser. Именно SAXParser дает вам XMLReader.
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.XMLReader;
public class SurveyReader extends DefaultHandler
{
public SurveyReader() {
}
public static void main (String args[]) {
XMLReader xmlReader = null;
try {
SAXParserFactory spfactory = SAXParserFactory.newInstance();
SAXParser saxParser = spfactory.newSAXParser();
xmlReader = saxParser.getXMLReader();
} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
}
Проверяющий парсер против непроверяющего
order должен иметь status. Если вы попытаетесь определить order без status, проверяющий парсер просигнализироет об ошибке.
Установка опций проверки правильности
SAXParserFactory, путем установки свойства validating:
...
public static void main (String args[]) {
XMLReader xmlReader = null;
try {
SAXParserFactory spfactory =
SAXParserFactory.newInstance();
spfactory.setValidating(false);
SAXParser saxParser =
spfactory.newSAXParser();
xmlReader = saxParser.getXMLReader();
} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
...
Установка обработчика содержимого
ContentHandler. Для простоты SurveyReader является и главным приложением, и обработчиком событий, так что создайте его новый экземпляр и установите его как ContentHandler, используя метод setContentHandler() класса XMLReader:
...
xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(new SurveyReader());
} catch (Exception e) {
...
Разбор InputSource
InputSource. Этот класс SAX является оболочкой для любых данных, которые вы собираетесь обрабатывать, так что вам не надо беспокоиться (слишком сильно) о том, откуда они поступают.
parse() принимает файл в оболочке InputSource и обрабатывает его, посылая каждое событие в ContentHander.
...
import org.xml.sax.InputSource;
...
xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(new SurveyReader());
InputSource source = new InputSource("surveys.xml");
xmlReader.parse(source);
} catch (Exception e) {
...
Установка ErrorHandler
ErrorHandler, но для простоты примера мы включим обработчик ошибок прямо в SurveyResults. Такое двойное его использование возможно потому, что класс расширяет DefaultHandler, который включает в себя реализации и метода ContentHandler, и метода ErrorHandler.
ErrorHandler точно так же, как вы установили ContentHandler:
...
xmlReader.setContentHandler(new SurveyReader());
xmlReader.setErrorHandler(new SurveyReader());
InputSource source = new InputSource("surveys.xml");
...
Раздел 4. Обработчики событий и события SAX
Теперь, когда вы сделали все установки для разбора документа, пришло время заменить реализации по умолчанию, которые являются частью класса startDocument()DefaultHandler, методами, которые на самом деле что-то делают, когда обработчик получает соответствующее событие.
Начните с того, что в начале документа ничего не делается при помощи события startDocument(). Это событие, как и другие события SAX, выбрасывает SAXException:
...
import org.xml.sax.SAXException;
public class SurveyReader extends DefaultHandler
{
public SurveyReader() {
}
public void startDocument() throws SAXException {
System.out.println("Tallying survey results...");
}
public static void main (String args[]) {
XMLReader xmlReader = null;
...
startElement()startElement() (см. копию экрана для списка элементов, показанную ниже.).
qName. Это комбинация информации пространства имен, если оно существует, и собственно имени элемента. qName также включает в себя двоеточие (:), если оно есть - например, revised:response.
http://www.nicholaschase.com/surveys/revised/ не то же, что просто revised:.
qName является localName.
...
import org.xml.sax.Attributes;
public class SurveyReader extends DefaultHandler
{
...
public void startDocument() throws SAXException {
System.out.println("Tallying survey results...");
}
public void startElement(String namespaceURI, String localName,
String qName, Attributes atts) throws SAXException {
System.out.print("Start element: ");
System.out.println(qName);
}
public static void main (String args[]) {
...
startElement(): выборка атрибутов startElement() также обеспечивает доступ к атрибутам элемента. Они передаются внутри структуры данных Attributes.
...
public void startElement(String namespaceURI, String localName,
String qName, Attributes atts) throws SAXException {
System.out.print("Start element: ");
System.out.println(qName);
for (int att = 0; att < atts.getLength(); att++) {
String attName = atts.getQName(att);
System.out.println(" " + attName + ": " + atts.getValue(attName));
}
}
...
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;
}
...
characters()characters(). Взглянем на сигнатуру этого метода:
public void characters(char[] ch,
int start,
int length)
characters() содержит больше, чем просто строку символов. Оно также содержит информацию о начале и о длине. На самом деле массив ch содержит полный документ. Приложение не должно пытаться читать символы за пределами размера, переданного в событие characters().
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));
}
}
...
Учет ответов
...
String appearance = null;
String communication = null;
String ship = null;
String inside = null;
String implant = null;
public void characters(char[] ch,
int start,
int length)
throws SAXException {
if (thisElement == "question") {
if (thisQuestion.equals("appearance")) {
appearance = appearance + new String(ch, start, length);
}
if (thisQuestion.equals("communication")) {
communication = communication + new String(ch, start, length);
}
if (thisQuestion.equals("ship")) {
ship = ship + new String(ch, start, length);
}
if (thisQuestion.equals("inside")) {
inside = inside + new String(ch, start, length);
}
if (thisQuestion.equals("implant")) {
implant = implant + new String(ch, start, length);
}
}
}
...
characters(), является то, что ответы, которые вы рассматриваете, имеют длину только в один символ. Если содержимое, которое вы рассматриваете, длиннее, вы должны собрать данные из каждого вызова вместе и анализировать их в конце элемента, в методе endElement().
ignorableWhitespace()characters(), а в событии ignorableWhitespace(). Это удобно, поскольку вы можете сосредоточиться только на реальных данных.
xml:space, и он обычно предполагается default. (Это значит, что поведение процессора по умолчанию состоит в игнорировании пропусков.)
preserve так:
<code-snippet xml:space="preserve">
<line> public void endElement(</line>
<line> String namespaceURI,</line>
<line> String localName,</line>
<line> String qName)</line>
<line> throws SAXException</line>
</code-snippet>
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[]) {
...
processingInstruction()
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="survey.xsl" version="1.0" type="text/xsl" ?>
<surveys>
...
<?xml version="1.0" encoding="UTF-8"?>
<?SurveyReader factor="2" ?>
<surveys>
...
processingInstruction(), которое разделит ее на target и data:
...
public void processingInstruction(String target, String data)
throws SAXException {
System.out.println("Target = ("+target+")");
System.out.println("Data = ("+data+")");
}
...
События
ErrorHandlerContentHandler имеет предопределенные события для обработки содержимого, ErrorHandler имеет предопределенные события для обработки ошибок. Поскольку вы определили в качестве и обработчика ошибок, и обработчика содержимого SurveyReader, вам нужно заместить реализацию этих методов по умолчанию.
warning, error и fatalError:
...
import org.xml.sax.SAXParseException;
public class SurveyReader
extends DefaultHandler
{
public SurveyReader() {
}
public void error (SAXParseException e) {
System.out.println("Error parsing the file: "+e.getMessage());
}
public void warning (SAXParseException e) {
System.out.println("Problem parsing the file: "+e.getMessage());
}
public void fatalError (SAXParseException e) {
System.out.println("Error parsing the file: "+e.getMessage());
System.out.println("Cannot continue.");
System.exit(1);
}
...
Раздел 5. SAX и пространства имен
Пространства имен
Создание пространства имен
xmlns:
<?xml version="1.0"?>
<surveys xmlns="http://www.nicholaschase.com/surveys/" >
<response username="bob">
<question subject="appearance">A</question>
...
http://www.nicholaschase.com/orderSystem.html. В действительности сам URI ничего не означает. Информация может находиться или не находиться по этому адресу, но что важно, так это то, что он уникален.
Определение пространств имен
revised вы можете добавить второй набор данных - скажем, о постгипнотическом состоянии, - не беспокоясь об имеющихся данных.
<?xml version="1.0"?>
<surveys xmlns="http://www.nicholaschase.com/surveys/"
xmlns:revised="http://www.nicholaschase.com/surveys/revised/">
<response username="bob">
<question subject="appearance">A</question>
<question subject="communication">B</question>
<question subject="ship">A</question>
<question subject="inside">D</question>
<question subject="implant">B</question>
<revised:question subject="appearance">D</revised:question>
<revised:question subject="communication">A</revised:question>
<revised:question subject="ship">A</revised:question>
<revised:question subject="inside">D</revised:question>
<revised:question subject="implant">A</revised:question>
</response>
<response username="sue">
...
revised, использованы для создания дополнительного элемента question.
revised - не пространство имен, а алиас! Настоящее пространство имен - http://www.nicholaschase.com/surveys/revised/.
Проверка пространств имен
thisElement не "question", вы должны просто выполнить проверку перед установкой переменной.
...
public void startElement(
String namespaceURI,
String localName,
String qName,
Attributes atts)
throws SAXException {
if (namespaceURI ==
"http://www.nicholaschase.com/surveys/") {
if (localName == "question") {
thisQuestion = atts.getValue("subject");
}
}
thisElement = localName;
}
...
Пространства имен на атрибутах
revised:subject, так:
<?xml version="1.0"?>
<surveys xmlns="http://www.nicholaschase.com/surveys/"
xmlns:revised="http://www.nicholaschase.com/surveys/revised/">
<response username="bob">
<question subject="appearance">A</question>
<question subject="communication">B</question>
<question subject="ship">A</question>
<question subject="inside">D</question>
<question subject="implant">B</question>
<revised:question subject="appearance"
revised:subject="looks" >D</revised:question>
<revised:question subject="communication">A</revised:question>
<revised:question subject="ship">A</revised:question>
<revised:question subject="inside">D</revised:question>
<revised:question subject="implant">A</revised:question>
</response>
<response username="sue">
...
response находится в пространстве имен
http://www.nicholaschase.com/surveys/, но его атрибут username - нет. Эта странность определена в самой Рекомендации XML.
Attributes имеет методы, которые позволяют вам определять пространство имен атрибута. Эти методы, getURI() и getQName, используются так же, как методы qname и localName для самого элемента.
Странности пространств имен
...
try {
SAXParserFactory spfactory = SAXParserFactory.newInstance();
spfactory.setValidating(false);
spfactory.setFeature("http://xml.org/sax/features/namespace-prefixes",
true);
spfactory.setFeature("http://xml.org/sax/features/namespaces",
true);
SAXParser saxParser = spfactory.newSAXParser();
...
Раздел 6. Работа с потоком SAX
Сериализация потока SAX
Serializer:
import org.apache.xalan.serialize.Serializer;
import org.apache.xalan.serialize.SerializerFactory;
import org.apache.xalan.templates.OutputProperties;
import java.io.FileOutputStream;
...
public static void main (String args[]) {
XMLReader xmlReader = null;
try {
SAXParserFactory spfactory = SAXParserFactory.newInstance();
spfactory.setValidating(false);
SAXParser saxParser = spfactory.newSAXParser();
xmlReader = saxParser.getXMLReader();
Serializer serializer = SerializerFactory.getSerializer(
OutputProperties.getDefaultMethodProperties("xml"));
serializer.setOutputStream(new FileOutputStream("output.xml"));
xmlReader.setContentHandler(
serializer.asContentHandler()
);
InputSource source = new InputSource("surveys.xml");
xmlReader.parse(source);
} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
...
Serializer - допустимыми значениями для OutputProperties являются xml, text и html - и установите его OutputStream. Этот поток может быть виртуально любым объектом потокового типа, таким как файл (как здесь) или System.out.
Serializer как обработчик содержимого для парсера, так что, когда парсер разбирает файл, именно Serializer принимает события.
XMLFilters
XMLFilter. Хотя они являются новшеством версии SAX 2.0, tони на самом деле использовались и в версии 1.0 умными программистами, которые реализовывали их путем соединения потоков SAX в цепочку и манипулирования ими прежде, чем они достигнут финального приемника.
XMLFilter. Обычно это - отдельный класс.
XMLFilter и установите его предком XMLReader, который будет обычным образом разбирать файл.
XMLReader и обработчиком содержимого.
Создание фильтра
SurveyReader сможет подхватить их.
XMLFilterImpl.
startElement(), оно проверяет URI исходного пространства имен. Если это элемент revised, пространство имен изменяется на пространство имен по умолчанию. Если нет, имя элемента изменяется так, что процедура подсчета (в SurveyReader) не будет распознавать его как вопрос и, следовательно, не будет учитывать ответ.
startElement() для предка (исходного XMLReader), и ими занимается его обработчик содержимого.
import org.xml.sax.helpers.XMLFilterImpl;
import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;
import org.xml.sax.Attributes;
public class SurveyFilter extends XMLFilterImpl
{
public SurveyFilter ()
{}
public SurveyFilter (XMLReader parent)
{
super(parent);
}
public void startElement (String uri,
String localName,
String qName,
Attributes atts)
throws SAXException
{
if (uri == "http://www.nicholaschase.com/surveys/revised/") {
uri = "http://www.nicholaschase.com/surveys/";
qName = "question";
} else {
localName = "REJECT";
}
super.startElement(uri, localName, qName, atts);
}
}
Вызов фильтра
XMLReader.
XMLReader.
XMLReader.
XMLReader определен как предок фильтра, он будет по-прежнему обрабатывать информацию.
...
public static void main (String args[]) {
XMLReader xmlReader = null;
try {
SAXParserFactory spfactory =
SAXParserFactory.newInstance();
spfactory.setValidating(false);
SAXParser saxParser =
spfactory.newSAXParser();
xmlReader = saxParser.getXMLReader();
SurveyFilter xmlFilter = new SurveyFilter();
xmlFilter.setParent(xmlReader);
xmlFilter.
setContentHandler(new SurveyReader());
xmlFilter.
setErrorHandler(new SurveyReader());
InputSource source = new InputSource("surveys.xml");
xmlFilter.
parse(source);
} catch (Exception e) {
System.err.println(e);
System.exit(1);
}
}
...
Использование XMLFilter для преобразования данных
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.
Раздел 7. Итоги по SAX
Резюме
Ресурсы
Обучение
Получение продуктов и технологий
Об авторе
| Каталог | Индекс раздела |