В статье суммируется мой небольшой опыт, связанный с использованием языка xml для построения внутрипрограммных служб. Сразу нужно отметить, что меня больше заботила не только работа с xml-документами, которые представлены как файлы. Значительно большее внимание уделялось использованию xml для формирования межмодульных интерфейсов в том случае, если взаимодействующие модули:
Наиболее важное свойство xml состоит в возможности описания сложных структур данных в машинно-независимом текстовом формате. Конечно, за такую универсальность приходится платить, но положительный эффект, который дает эта технология, намного превосходит издержки в том случае, когда наиболее важными факторами оказываются гибкость, легкость модификации, переносимость.
В статье описана реализация различных механизмов, которые позволяют как строить xml-запросы и документы, так и выполнять их синтаксический анализ (разбор xml-текстов). Все описываемые механизмы входят в состав библиотеки GsvXml. Библиотека доступна как Freeware и распространяется в исходных кодах. В библиотеке реализованы упрощенные варианты:
Скоростные характеристики парсеров обоих типов (dom и sax) примерно одинаковы. На моей машине (Pentium-800) Sax-парсер имеет скорость 11 мегабайт в секунду при работе с текстом, находящимся в оперативной памяти, скорость Dom-парсера - 8 мегабайт в секунду.
Исходный текст для Dom-парсера может состоять из одного или нескольких полных xml-документов, причем каждый отдельный xml-документ может содержать собственный пролог. Sax-парсер ориентирован на обработку непрерывного потока xml-документов, причем поток может быть произвольно разбит на фрагменты (не обязательно по границе документов). Существует ряд отклонений от стандарта XML, которые перечислены ниже.
Значение элемента и атрибута обрабатываются по следующим правилам:
Класс является базовым для всех производных классов-читателей, содержит одну виртуальную абстрактную функцию GetChar, которая возвращает очередной символ из входного потока.
Класс является базовым для всех производных классов-писателей, и содержит несколько виртуальных абстрактных процедур для вывода символа PutChar, строки PutString или буфера PutBuffer(aBuffer: PChar; aSize: Cardinal)
Буфер, используемый в остальных классах для выполнения буферизованного строкового ввода-вывода. Основные методы класса:
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;
Класс читателя, получающего символы из текстовой строки. Класс инкапсулирует буфер TGsvXmlBuffer и читает входные данные с его помощью.
Класс писателя, формирующего результат в виде текстовой строки с помощью внутреннего буфера TGsvXmlBuffer.
Специализированный класс, наследуемый от TGsvXmlStringWriter и ориентированный на запись результата в файл
Базовый класс для построителей xml-текста, имеет конструктор и одно открытое свойство:
constructor Create(aWriter: TGsvXmlWriter; aFormat: TGsvXmlWriterFormat = wfNone); property Format: TGsvXmlWriterFormat read FFormat write FFormat;
Формат выходного xml-текста задается перечислением, указывающим варианты начальных отступов для вложенных узлов - wfNone (без отступов), wfSpace (один пробел), wfDoubleSpace (два пробела), wfTab (табуляция).
Класс, наследуемый от 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"?>
Класс, который строит 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;
Класс 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;
Класс 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;
Базовый класс для 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;
Dom-парсер формирует результат разбора как дерево узлов, класс определяет виртуальные абстрактные методы базового класса и добавляет открытый метод и свойство:
procedure Parse; property Root: TGsvXmlNode read FRoot;
Процедура Parse выполняет полный разбор потока, заданного читателем Reader при конструировании класса, а свойство Root возвращает корневой узел, который содержит все распознанные узлы.
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;
Классы являются специализацией 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;
delphixml.zip - Исходные коды (12K).