Матрёшка: использование SAX API для обработки XML потоков
SAX API это один из видов стандартов де‐факто на обработку XML документов. В силу своей огранизации он хорошо подходит не только для обработки XML документов, но для обработки XML потоков, поскольку не требует загрузки полного документа. Матрёшка предоставляет реализацию невалидирующего парсера XML с SAX API.
Архитектура SAX
Архитектурно SAX состоит из четырёх базовых компонентов:
- «читатель» (SAX_Reader) — выполняет разбор XML потока;
- источник данных (SAX_Input_Source) — предоставляет «читателю» данные для разбора, абстрагируя таким образом сам источник данных;
- обработчики (их много) — предоставляемые приложением компоненты, вызываемые «читателем» для извещения об обнаружении тех или иных конструкций;
- разрешитель внешних сущностей (SAX_Entity_Resolver) — позволяет приложению менять источник внешних документов, например, предоставляя локальные копии вместо глобальных.
Обработчики
Обработчики по своей сути есть предоставляемые приложением подпрограммы, вызываемые читателем при распозновании в XML потоке тех или иных конструкций. Для удобства использования они сгруппированы по назначению и оперделены как примитивные операции интерфейсных типов:
- SAX_Content_Handler — основной интерфейсный тип, используемый каждым приложением. Его подпрограммы вызываются при начале/завершении обработки документа, обнаружении открывыающего или закрывающего тегов, текстовых данных и т.д;
- SAX_Declaration_Handler — один из обработчиков элементов определения типа документа (DTD — Document Type Definition). Его подпрограммы вызываются при при обнаружении объявления атрибута, а так же при обнаружении объявления внешей или внутренней сущности;
- SAX_DTD_Handler — второй обработчик элементов определения типа документа. Его продпрограммы вызываются при обнаружении объявления нотации и внешней неанализируемой сущности;
- SAX_Error_Handler — обработчик ошибок XML. Его методы вызываются при обнаружении каких либо ошибок в потоке;
- SAX_Lexical_Handler — обработчик лексических конструкций, таких как начало/конец определения типа документа, комментарии, начало/конец секции CDATA, и т.д.
Обработка ошибок
При обработке XML документа возможно возникновение ошибок, как связанных с некорректностью XML документа, так и обнаруженных приложением. Для управления обработкой XML документа Матрёшка предоставляет унифицированный механизм, предоставляющий приложению контролировать поведение в случае обнаружения ошибок. Первое, что необходимо отметить особо — ни один компонент никогда не возбуждает Ada исключения; этого же рекомендуется придерживаться и разработчикам приложений. При возникновении исключения в обработчике оно будет перехвачено и обработано как фатальная ошибка, однако это не приведёт к каким‐либо нежелательным последствиям.
Каждая подпрограмма обработчик имеет специальный параметр (Success) для контроля дальнейшей обработки. При обнаружении ошибки приложения, требующей немедленного останова обработки этот параметр должен быть установлен в False; а подпрограмма Error_String должна быть готова вернуть текстовое описание обнаруженной ошибки. Это текстовое описание будет извлечено читателем и использовано для передачи приложению, запустившему процесс чтения документа.
При возникновении ошибок в XML документе читатель вызывает одну из трёх подпрограмм интерфейсного типа SAX_Error_Handler в зависимости от степени фатальности ошибки. Подпрограммы этого интерфейсного типа так же поддерживают описанный выше механизм обработки ошибок, позволяя приложению остановить обработку и предоставить собственное диагностическое сообщение даже при незначительных ошибках в XML документе.
Контроль за используемыми ресурсами
Реализация SAX в Матрёшке максимально избавляет разработчика приложения от необходимости управления освобождением ресурсов. Это достигается как за счёт использования специальных типов данных, автоматически освобождающих использованные ресурсы, так и за счёт некоторых соглашений. В частности, разработчик должен иметь в виду:
- все источники данных, созданные по запросу читателя с использованием интерфейса SAX_Entity_Resolver освобождаются автоматически; однако
- начальный источник данных, предоставленный читателю приложением, не будет освобождаться автоматически (поскольку его часто удобно размещать на стэке);
- объекты предоставленные приложением для обработки событий и разрешения сущностей не будет освобождаться автоматически.
Источники данных
Матрёшка предоставляет ряд источников данных, однако при необходимости приложение может предоставить собственную реализацию. Имеющиеся реализации покрывают значительную часть потребностей приложений:
- XML.SAX.Input_Sources.Strings — использует предоставленную приложением строку, содержащую текст XML документа;
- XML.SAX.Input_Sources.Streams.Files — использует данные из файла;
- XML.SAX.Input_Sources.Streams.Sockets — позволяет извлекать данные из сокета сетевого соединения.
Небольшой пример
Ну вот и пришло время привести небольшой пример приложения, использующего SAX Матрёшки для разбора документа. Сначала рассмотрим реализацию обработчика, обрабатывающего начало элементов и ошибки XML документа. Спецификция пакета обработчика будет выглядеть так:
with League.Strings;
with XML.SAX.Attributes;
with XML.SAX.Content_Handlers;
with XML.SAX.Error_Handlers;
with XML.SAX.Parse_Exceptions;
package Handlers is
type Handler is
limited new XML.SAX.Content_Handlers.SAX_Content_Handler
and XML.SAX.Error_Handlers.SAX_Error_Handler with private;
overriding procedure Start_Element
(Self : in out Handler;
Namespace_URI : League.Strings.Universal_String;
Local_Name : League.Strings.Universal_String;
Qualified_Name : League.Strings.Universal_String;
Attributes : XML.SAX.Attributes.SAX_Attributes;
Success : in out Boolean);
overriding procedure Error
(Self : in out Handler;
Occurrence : XML.SAX.Parse_Exceptions.SAX_Parse_Exception;
Success : in out Boolean);
overriding procedure Fatal_Error
(Self : in out Handler;
Occurrence : XML.SAX.Parse_Exceptions.SAX_Parse_Exception;
Success : in out Boolean);
overriding procedure Warning
(Self : in out Handler;
Occurrence : XML.SAX.Parse_Exceptions.SAX_Parse_Exception;
Success : in out Boolean);
overriding function Error_String
(Self : Handler) return League.Strings.Universal_String;
private
type Handler is
limited new XML.SAX.Content_Handlers.SAX_Content_Handler
and XML.SAX.Error_Handlers.SAX_Error_Handler with
record
end Handlers;
А реализация так:
with Ada.Wide_Wide_Text_IO;
package body Handlers is
overriding procedure Error
(Self : in out Handler;
Occurrence : XML.SAX.Parse_Exceptions.SAX_Parse_Exception;
Success : in out Boolean) is
begin
Ada.Wide_Wide_Text_IO.Put_Line ("error");
end Error;
overriding function Error_String
(Self : Handler) return League.Strings.Universal_String is
begin
return Self.Error_String;
end Error_String;
overriding procedure Fatal_Error
(Self : in out Handler;
Occurrence : XML.SAX.Parse_Exceptions.SAX_Parse_Exception;
Success : in out Boolean) is
begin
Ada.Wide_Wide_Text_IO.Put_Line ("fatal error");
end Fatal_Error;
overriding procedure Start_Element
(Self : in out Handler;
Namespace_URI : League.Strings.Universal_String;
Local_Name : League.Strings.Universal_String;
Qualified_Name : League.Strings.Universal_String;
Attributes : XML.SAX.Attributes.SAX_Attributes;
Success : in out Boolean) is
begin
Ada.Wide_Wide_Text_IO.Put_Line
(Qualified_Name.To_Wide_Wide_String);
end Start_Element;
overriding procedure Warning
(Self : in out Handler;
Occurrence : XML.SAX.Parse_Exceptions.SAX_Parse_Exception;
Success : in out Boolean) is
begin
Ada.Wide_Wide_Text_IO.Put_Line ("warning");
end Warning;
end Handlers;
Теперь необходимо написать основное приложение, использущее обработчик:
with League.Application;
with XML.SAX.Input_Sources.Streams.Files;
with XML.SAX.Simple_Readers;
with Handlers;
procedure Main is
Handler : aliased Handlers.Handler;
Input : aliased
XML.SAX.Input_Sources.Streams.Files.File_Input_Source;
Reader : aliased XML.SAX.Simple_Readers.SAX_Simple_Reader;
begin
Input.Open_By_File_Name (League.Application.Arguments.Element (1));
Reader.Set_Content_Handler (Handler'Unchecked_Access);
Reader.Set_Error_Handler (Handler'Unchecked_Access);
Reader.Parse (Input'Unchecked_Access);
end Main;
Автор: Вадим Годунко
Дата: 03.04.2012
|