Матрёшка: использование 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

   -----------
   -- Error --
   -----------

   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;

   ------------------
   -- Error_String --
   ------------------

   overriding function Error_String
    (Self : Handler) return League.Strings.Universal_String is
   begin
      return Self.Error_String;
   end Error_String;

   -----------------
   -- Fatal_Error --
   -----------------

   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;

   -------------------
   -- Start_Element --
   -------------------

   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;

   -------------
   -- Warning --
   -------------

   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
   --  Open source document.

   Input.Open_By_File_Name (League.Application.Arguments.Element (1));

   --  Configure reader.

   Reader.Set_Content_Handler (Handler'Unchecked_Access);
   Reader.Set_Error_Handler (Handler'Unchecked_Access);

   --  Parse document.

   Reader.Parse (Input'Unchecked_Access);
end Main;

Автор: Вадим Годунко
Дата: 03.04.2012