Работа с xml

Последнее обновление: 22 декабря 2005

В статье суммируется мой небольшой опыт, связанный с использованием языка xml для построения внутрипрограммных служб. Сразу нужно отметить, что меня больше заботила не только работа с xml-документами, которые представлены как файлы. Значительно большее внимание уделялось использованию xml для формирования межмодульных интерфейсов в том случае, если взаимодействующие модули:

Наиболее важное свойство xml состоит в возможности описания сложных структур данных в машинно-независимом текстовом формате. Конечно, за такую универсальность приходится платить, но положительный эффект, который дает эта технология, намного превосходит издержки в том случае, когда наиболее важными факторами оказываются гибкость, легкость модификации, переносимость.

В статье описана реализация различных механизмов, которые позволяют как строить xml-запросы и документы, так и выполнять их синтаксический анализ (разбор xml-текстов). Все описываемые механизмы входят в состав библиотеки GsvXml. Библиотека доступна как Freeware и распространяется в исходных кодах. В библиотеке реализованы упрощенные варианты:

Общая характеристика парсеров

Скоростные характеристики парсеров обоих типов (dom и sax) примерно одинаковы. На моей машине (Pentium-800) Sax-парсер имеет скорость 11 мегабайт в секунду при работе с текстом, находящимся в оперативной памяти, скорость Dom-парсера - 8 мегабайт в секунду.

Исходный текст для Dom-парсера может состоять из одного или нескольких полных xml-документов, причем каждый отдельный xml-документ может содержать собственный пролог. Sax-парсер ориентирован на обработку непрерывного потока xml-документов, причем поток может быть произвольно разбит на фрагменты (не обязательно по границе документов). Существует ряд отклонений от стандарта XML, которые перечислены ниже.

Значение элемента и атрибута обрабатываются по следующим правилам:

Основные классы

TGsvXmlReader

Класс является базовым для всех производных классов-читателей, содержит одну виртуальную абстрактную функцию GetChar, которая возвращает очередной символ из входного потока.

TGsvXmlWriter

Класс является базовым для всех производных классов-писателей, и содержит несколько виртуальных абстрактных процедур для вывода символа PutChar, строки PutString или буфера PutBuffer(aBuffer: PChar; aSize: Cardinal)

TGsvXmlBuffer

Буфер, используемый в остальных классах для выполнения буферизованного строкового ввода-вывода. Основные методы класса:

procedure Clear;
procedure Load(const FileName: string);
procedure Save(const FileName: string);
procedure SetText(Value: PChar; aSize: Cardinal);
  overload;
procedure SetText(const Value: string); overload;
procedure SetText(Value: PChar); overload;

function  GetText: PChar;
procedure AddChar(Value: Char);
procedure AddBuffer(aBuffer: PChar; aSize: Cardinal);
procedure AddString(const Value: string);
function  GetChar: Char;
function  PeekLastChar(out Value: Char): Boolean;

property Boost: Boolean read FBoost write FBoost;
property Capacity: Cardinal read FCapacity 
  write SetCapacity;
property MaxCapacity: Cardinal read FMaxCapacity
  write SetMaxCapacity;
property Size: Cardinal read FSize;
property Position: Cardinal read FPosition;
property Buffer: PChar read FBuffer;

TGsvXmlStringReader

Класс читателя, получающего символы из текстовой строки. Класс инкапсулирует буфер TGsvXmlBuffer и читает входные данные с его помощью.

TGsvXmlStringWriter

Класс писателя, формирующего результат в виде текстовой строки с помощью внутреннего буфера TGsvXmlBuffer.

TGsvXmlFileWriter

Специализированный класс, наследуемый от TGsvXmlStringWriter и ориентированный на запись результата в файл

TGsvXmlBuilder

Базовый класс для построителей xml-текста, имеет конструктор и одно открытое свойство:

constructor Create(aWriter: TGsvXmlWriter;
  aFormat: TGsvXmlWriterFormat = wfNone);
property Format: TGsvXmlWriterFormat read FFormat
  write FFormat;

Формат выходного xml-текста задается перечислением, указывающим варианты начальных отступов для вложенных узлов - wfNone (без отступов), wfSpace (один пробел), wfDoubleSpace (два пробела), wfTab (табуляция).

TGsvXmlDomBuilder

Класс, наследуемый от TGsvXmlBuilder, который строит xml-текст из готового Dom-дерева документа. Построение делается как одношаговая процедура сразу для всего дерева.

procedure Build(Root: TGsvXmlNode;
  const aProlog: string = '');
procedure BuildNode(Node: TGsvXmlNode;
  const aProlog: string = '');

Процедура Build создает xml-текст для корневого узла документа и всех его дочерних узлов, а BuildNode - только для конкретного узла и его дочерних узлов. В качестве пролога можно использовать константу GsvXmlProlog, соответствующую прологу:
<?xml version="1.0" encoding="windows-1251"?>

TGsvXmlSaxBuilder

Класс, который строит xml-текст без использования объектов Dom. Построение делается как многошаговая процедура, на каждом шаге которой формируется только часть элемента. Открытые методы класса:

constructor Create(aWriter: TGsvXmlWriter;
  aFormat: TGsvXmlWriterFormat = wfNone);

procedure AddInstruction(const Value: string);
procedure AddNode(const Name: string);
procedure AddAttribute(const Name, Value: string);
procedure AddValue(const Value: string);
procedure ConcatValue(const Value: string);
procedure EndNode;
procedure AddCDATA(const Value: string);
procedure ConcatCDATA(const Value: string);
procedure EndCDATA;

TGsvXmlAttribute

Класс Dom-атрибута, имеет следующие свойства:

property Parent: TGsvXmlNode read FParent;
property Next: TGsvXmlAttribute read FNext;
property Name: string read FName write FName;
property Value: string read FValue write FValue;

TGsvXmlNode

Класс Dom-узла (элемента), имеет следующие открытые методы и свойства:

constructor Create; overload;
constructor Create(const aName: string); overload;
constructor Create(const aName, aValue: string); 
  overload;

procedure Clear;
function AddChild: TGsvXmlNode; overload;
function AddChild(const aName: string):
  TGsvXmlNode; overload;
function AddChild(const aName, aValue: string):
  TGsvXmlNode; overload;
function AddAttribute: TGsvXmlAttribute; overload;
function AddAttribute(const aName: string): 
  TGsvXmlAttribute; overload;
function AddAttribute(const aName, aValue: string):
  TGsvXmlAttribute; overload;
function FindChild(const aName: string): 
  TGsvXmlNode;
function FindAttribute(const aName: string): 
  TGsvXmlAttribute;
procedure DeleteChild(const aName: string); overload;
procedure DeleteChild(aNode: TGsvXmlNode); overload;
procedure DeleteAttribute(const aName: string); overload;
procedure DeleteAttribute(aAttribute: TGsvXmlAttribute); 
  overload;

property Parent: TGsvXmlNode read FParent;
property Next: TGsvXmlNode read FNext;
property FirstChild: TGsvXmlNode read FFirstChild;
property FirstAttribute: TGsvXmlAttribute 
  read FFirstAttribute;
property Name: string read FName write FName;
property Value: string read FValue write FValue;

TGsvXmlParser

Базовый класс для xml-парсеров. Класс имеет следующие основные защищенные и открытые методы:

protected
  procedure OnInstructionHandler(aValue:
    TGsvXmlBuffer); virtual; abstract;
  procedure OnNodeHandler(aName:
    TGsvXmlBuffer); virtual; abstract;
  procedure OnAttributeHandler(aName,
    aValue: TGsvXmlBuffer); virtual; abstract;
  procedure OnValueHandler(aValue:
    TGsvXmlBuffer); virtual; abstract;
  procedure OnEndNodeHandler; virtual; abstract;

public
  constructor Create(Reader: TGsvXmlReader);

  procedure Clear;
  procedure NextParse;

TGsvXmlDomParser

Dom-парсер формирует результат разбора как дерево узлов, класс определяет виртуальные абстрактные методы базового класса и добавляет открытый метод и свойство:

procedure Parse;
property Root: TGsvXmlNode read FRoot;

Процедура Parse выполняет полный разбор потока, заданного читателем Reader при конструировании класса, а свойство Root возвращает корневой узел, который содержит все распознанные узлы.

TGsvXmlSaxParser

Sax-парсер формирует результат разбора в виде вызовов обработчиков событий:

procedure Parse;

property OnInstruction: TGsvXmlInstructionHandler
  read FOnInstruction write FOnInstruction;
property OnNode: TGsvXmlNodeHandler
  read FOnNode write FOnNode;
property OnAttribute: TGsvXmlAttributeHandler
  read FOnAttribute write FOnAttribute;
property OnValue: TGsvXmlValueHandler
  read FOnValue write FOnValue;
property OnEndNode: TGsvXmlEndNodeHandler
  read FOnEndNode write FOnEndNode;

TGsvXmlDomFileParser и TGsvXmlSaxFileParser

Классы являются специализацией Dom и Sax-парсера применительно к входному xml-тексту из файла.

Примеры

Чтение xml-файла и формирование дерева узлов по Dom-модели:

procedure ReadDom(aFileName: string);
var
  FReader:    TGsvXmlStringReader;
  FDomParser: TGsvXmlDomParser;
  root:       TGsvXmlNode;
begin
  FReader := TGsvXmlStringReader.Create;
  try
    FDomParser :=
      TGsvXmlDomParser.Create(FReader);
    try
      FReader.Buffer.Load(aFileName);
      FDomParser.Parse;
      root := FDomParse.Root;
      // работаем с узлами дерева
      .....
    finally
      FDomParser.Free;
    end;
  finally
    FReader.Free;
  end;
end;

Формирование дерева узлов и запись результата в xml-файл:

procedure WriteDom(aFileName: string);
var
  FWriter:     TGsvXmlStringWriter;
  FDomBuilder: TGsvXmlDomBuilder;
  root:        TGsvXmlNode;
begin
  FWriter := TGsvXmlStringWriter.Create;
  try
    FDomBuilder := TGsvXmlDomBuilder.Create(
      FWriter, wfDoubleSpace);
    try
      root := TGsvXmlNode.Create;
      try
        // создаем дерево узлов
        ....
        FDomBuilder.Build(root, GsvXmlProlog);
        FWriter.Buffer.Save(aFileName);
      finally
        root.Free;
      end;
    finally
      FDomBuilder.Free;
    end;
  finally
    FWriter.Free;
  end;
end;

Download

Downloaddelphixml.zip - Исходные коды (12K).