Delphi - объектно-ориентированный язык программирования, разработанный компанией Borland в 1995 году. Он основан на языке программирования Pascal, но имеет более расширенные возможности и добавлены новые функции.
Delphi является интегрированной средой разработки (IDE), которая позволяет разрабатывать программное обеспечение для различных платформ, включая Windows, macOS, Android и iOS. Delphi достигает многоплатформенности с помощью...
{ **** UBPFD *********** by delphibase.endimus.com ****
>> Парсер подавляющего большинства нотаций XML.
Для задачи десериализации мне потребовался парсер.
Основное преимущество - никак не связан с операционной системой
(в отличие от TXMLDocument), ну и разумеется - простота :)
Зависимости: SysUtils, StrUtils
Автор: Delirium, VideoDVD@hotmail.com, ICQ:118395746, Москва
Copyright: Delirium (Master BRAIN) 2003
Дата: 16 сентября 2003 г.
***************************************************** }
unit BNFXMLParser;
interface
uses
SysUtils, StrUtils;
type
PXMLNode = ^TXMLNode;
TXMLValues = (TextNode, XMLNode);
TXMLNode = record
Name: string
;
Attributes: array
of
record
Name: string
;
Value: string
;
end
;
SubNodes: array
of
record
RecType: TXMLValues;
case
TXMLValues of
TextNode: (Text: PString);
XMLNode: (XML: PXMLNode);
end
;
Parent: PXMLNode;
end
;
function
BNFXMLTree(var
Value: string
): PXMLNode;
implementation
function
fnTEG(var
Node: PXMLNode; var
Value: string
): boolean; forward
;
function
fnVAL(var
Node: PXMLNode; var
Value: string
): boolean; forward
;
function
fnATT(var
Node: PXMLNode; var
Value: string
): boolean; forward
;
function
fnXML(var
Node: PXMLNode; var
Value: string
): boolean;
var
i: integer;
begin
if
(Pos('<', Value) > 0)
and
(Pos('>', Value) > Pos('<', Value))
and
(Pos('<', Value) <> Pos('</', Value)) then
begin
// Оганизую узел
if
Node = nil
then
begin
New(Node);
Node.Parent := nil
;
end
else
begin
i := length(Node.SubNodes);
Setlength(Node.SubNodes, i + 1);
New(Node.SubNodes[i].XML);
Node.SubNodes[i].RecType := XMLNode;
Node.SubNodes[i].XML.Parent := Node;
Node := Node.SubNodes[i].XML;
end
;
Result := fnTEG(Node, Value);
end
// '<'
else
Result := True;
end
;
function
fnTEG(var
Node: PXMLNode; var
Value: string
): boolean;
var
i, i1, i2, i3: integer;
S: string
;
begin
Result := False;
i1 := Pos('<', Value);
if
i1 > 0 then
begin
i2 := PosEx('/>', Value, i1);
i3 := PosEx('>', Value, i1);
if
(i2 > 0) and
(i2 < i3) then
begin
// <abc/>
// Value
S := Copy(Value, i1 + 1, (i2 - i1) - 1);
Delete(Value, i1, (i2 - i1) + 2);
// TEXT, этот текст пренадлежит предку
if
Node.Parent <> nil
then
begin
// Добавляюсь к предку
i := length(Node.Parent.SubNodes);
Setlength(Node.Parent.SubNodes, i + 1);
New(Node.Parent.SubNodes[i].Text);
Node.Parent.SubNodes[i].RecType := TextNode;
Node.Parent.SubNodes[i].Text^ := Copy(Value, 1, Pos('<', Value) - 1);
end
;
Delete(Value, 1, Pos('<', Value) - 1);
//
if
fnVAL(Node, S) then
begin
// Вложенных тегов не бывает
Node := Node.Parent;
Result := fnXML(Node, Value);
end
;
end
else
begin
// <abc>...</abc>
// Value
S := Copy(Value, i1 + 1, (i3 - i1) - 1);
Delete(Value, i1, (i3 - i1) + 1);
// TEXT
i := length(Node.SubNodes);
Setlength(Node.SubNodes, i + 1);
New(Node.SubNodes[i].Text);
Node.SubNodes[i].RecType := TextNode;
Node.SubNodes[i].Text^ := Copy(Value, 1, Pos('<', Value) - 1);
Delete(Value, 1, Pos('<', Value) - 1);
//
if
fnVAL(Node, S) then
begin
// Val
// Проверяю закрытие тега, удаляю хвост и передаю управление предку
if
Pos('</' + AnsiLowerCase(Node.Name) + '>', AnsiLowerCase(Value)) = 1
then
begin
Delete(Value, 1, Length('</' + Node.Name + '>'));
// TEXT принадлежащий предку
if
Node.Parent <> nil
then
begin
// Добавляюсь к предку
i := length(Node.Parent.SubNodes);
Setlength(Node.Parent.SubNodes, i + 1);
New(Node.Parent.SubNodes[i].Text);
Node.Parent.SubNodes[i].RecType := TextNode;
Node.Parent.SubNodes[i].Text^ := Copy(Value, 1, Pos('<', Value) -
1);
end
;
Delete(Value, 1, Pos('<', Value) - 1);
Node := Node.Parent;
Result := fnXML(Node, Value);
end
else
begin
// Обрабатываю вложенные теги, на выходе мой узел
if
fnXML(Node, Value) then
begin
// закрываю его
if
Pos('</' + AnsiLowerCase(Node.Name) + '>', AnsiLowerCase(Value))
= 1 then
begin
Delete(Value, 1, Length('</' + Node.Name + '>'));
// TEXT принадлежащий предку
if
Node.Parent <> nil
then
begin
// Добавляюсь к предку
i := length(Node.Parent.SubNodes);
Setlength(Node.Parent.SubNodes, i + 1);
New(Node.Parent.SubNodes[i].Text);
Node.Parent.SubNodes[i].RecType := TextNode;
Node.Parent.SubNodes[i].Text^ := Copy(Value, 1, Pos('<', Value)
- 1);
end
;
Delete(Value, 1, Pos('<', Value) - 1);
end
;
// Остальной XML - предку
if
Node.Parent <> nil
then
Node := Node.Parent;
Result := fnXML(Node, Value);
end
;
end
;
end
; // Val
end
; // <abc>...</abc>
end
; // i1
end
;
function
fnVAL(var
Node: PXMLNode; var
Value: string
): boolean;
begin
Value := AnsiReplaceStr(Value, '''', '"');
if
(Pos(' ', Value) > 0)
and
(Pos('="', Value) > Pos(' ', Value)) then
begin
Node.Name := Trim(Copy(Value, 1, Pos(' ', Value) - 1)); // Название тега Name
Delete(Value, 1, Pos(' ', Value));
Result := fnATT(Node, Value);
end
// ' ' и ('="'
else
begin
// Название тега Name
Value := Trim(Value);
if
Pos(' ', Value) > 0 then
Node.Name := Copy(Value, 1, Pos(' ', Value) - 1)
else
Node.Name := Value;
Value := '';
Result := True;
end
;
end
;
function
fnATT(var
Node: PXMLNode; var
Value: string
): boolean;
begin
Result := True;
Value := Trim(Value);
if
Pos('="', Value) > 0 then
begin
Result := False;
SetLength(Node.Attributes, Length(Node.Attributes) + 1);
// Название атрибута
Node.Attributes[Length(Node.Attributes) - 1].Name := Trim(Copy(Value, 1,
Pos('="', Value) - 1));
Delete(Value, 1, Pos('="', Value) + 1);
if
Pos('"', Value) > 0 then
begin
// Значение атрибута
Node.Attributes[Length(Node.Attributes) - 1].Value := Copy(Value, 1,
Pos('"', Value) - 1);
Delete(Value, 1, Pos('"', Value));
if
Length(Value) > 0 then
Result := fnATT(Node, Value)
else
Result := True;
end
;
end
;
end
;
function
BNFXMLTree(var
Value: string
): PXMLNode;
begin
Result := nil
;
fnXML(Result, Value);
end
;
end
.
Пример использования:
procedureTForm1.Button1Click(Sender: TObject); var
S: string
; Node: PXMLNode; i: integer; begin
S := '<A> aaa1 ' + #13 + ' aaa2 aaa3 ' + #13 + ' <B>bbb ' + #13 + ' <C>ccc</C> ' + #13 + ' </B> ' + #13 + ' <D>ddd ' + #13 + ' <E eee="EEE"/> ' + #13 + ' </D> ' + #13 + '</A> '; Node := BNFXMLTree(S); for
i := 0 to
Length(Node.SubNodes) - 1 do
case
Node.SubNodes[i].RecType of
TextNode: ShowMessage('Text = ' + Node.SubNodes[i].Text^); XMLNode: ShowMessage('XML Node name = ' + Node.SubNodes[i].XML.Name); end
; end
;