уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Так как в последнее время очень много работаю с XML-документами различно сложности, то в процессе работы родилась небольшая идея — написать микро-приложение с помощью которого можно проследить логику документа в принципе, то есть посмотреть уровень вложенности узлов, в случае необходимости — отметить определенные узлы как-либо и так далее. Пока количество узлов и их атрибутов не велико, то с задачей вполне справиться любой браузер, но, когда у каждого узла по куче атрибутов, а сам документ имеет большую вложенность узлов, то как-то становиться жалко ломать глаза.Вот и родилась идея этого приложения, как вспомогательного инструмента для разбора XML и предоставления его в виде TreeView в Delphi.

Работать будем сегодня вот с чем:

  • модули Delphi xmldoc, XMLIntf;
  • XML-файл по-сложнее и по-больше
  • ну и конечно же TreeView.

Цель нашей работы — сопоставить каждому узлу в XML-документе узел в TreeView и по клику в дереве выводить, например, значения атрибутов узла.

Первое, что пришло в голову- это сделать запись вида:

type
  TNodeRecord = record
    XMLNode:  IXMLNode;
    TrNode: TTreeNode;
end;

Так, используя эту запись можно всегда найти узел XML по, например, имени узла в TreeView и наоборот.

Теперь, определим следующие типы данных и класс:

type
  PTreeView = ^TTreeView;
  PNodeRecord = ^TNodeRecord;
 
type
TNodeList = class(TList)
private
  procedure SetRecord(index: Integer; Ptr: PNodeRecord);
  function GetRecord(index: Integer): PNodeRecord;
public
  constructor Create;
  procedure Clear;
  destructor Destroy; override;
  property NodeItem[i: Integer]: PNodeRecord read GetRecord write SetRecord;
end;

В этом списке мы и будем хранить все наше «богатство». Если потребуется, то можно будет дописать, например дополнительные методы поиска по атрибутам и так далее.

Теперь непосредственно сам класс, реализующий работу с деревом и XML-документом:

type
TXMLTree = class
private
  FTreeView: PTreeView;//указатель на дерево
  FXMLDoc: IXMLDocument;//XML-документ
  FFileName: string;
  FNodeList: TNodeList;
  procedure LoadTopElements;
  procedure Recurse;
public
  function XMLNodeFromTreeText(const cText: string):IXMLNode;
  property NodeList: TNodeList read FNodeList;
  constructor Create(const aFileName:string; aTree:PTreeView);
end

На данный момент с использованием класса можно сделать следующее:

  1. Загрузить XML-документ и построить дерево TreeView
  2. Получить список всех узлов
  3. Получить целиком (вместе с атрибутами) узел XML-документа, которому соответствует узел TreeView c текстом cText
  4. Загрузить в TreeView только узлы первого уровня.

Дерево TreeView строится при этом рекурсивно:

procedure TXMLTree.Recurse;
var iNode: IXMLNode;
 
procedure ProcessNode(Node: IXMLNode; TreeNode: TTreeNode);
var cNode: IXMLNode;
    s: string;
NodeRec: PNodeRecord;
begin
  if Node = nil then Exit;
  s:=Node.NodeName;
  TreeNode := FTreeView.Items.AddChild(TreeNode, s);
  New(NodeRec);
    with NodeRec^ do
      begin
        XMLNode:=Node;
        TrNode:=TreeNode;
      end;
    FNodeList.Add(NodeRec);
end;
cNode := Node.ChildNodes.First;
while cNode <> nil do
begin
  ProcessNode(cNode, TreeNode);
  cNode := cNode.NextSibling;
end;
end;
 
begin
  iNode := FXMLDoc.DocumentElement.ChildNodes.First;//стартуем с первого элемента
  while iNode <> nil do
    begin
      ProcessNode(iNode, nil); // Рекурсия
      iNode:=iNode.NextSibling;
    end;
end;

Можно, конечно, поругаться на рекурсию, типа опасно и т.д., но в данном случае никаких зависаний, залипаний и других ошибок быть не должно. Проверял на достаточно сложных документах с большим количеством уровней, узлов и т.д. — работает шустро.

Поиск узла XML-документа по тексту в TreeView осуществляется следующим образом:

function TXMLTree.XMLNodeFromTreeText(const cText: string): IXMLNode;
var LCount: Integer;
    LList: PPointerList;
    i:integer;
begin
  LCount:= FNodeList.Count;
  LList :=FNodeList.List;
  for i := 0 to LCount - 1 do
    begin
      if PNodeRecord(LList[i])^.TrNode.Text=cText then
        begin
          Result:=PNodeRecord(LList[i])^.XMLNode;
          break;
        end;
    end;
end;

Теперь, что касается вообще работы с объектом. При построении дерева можно как угодно менять название узла в TreeView -функция поиска все равно будет работать.

Ну, а после того, как узел TreeView сопоставлен с IXMLNode — можно делать все что угодно, в том числе и читать атрибуты узла.

Кстати, чуть не забыл, те, кто желает ознакомиться с методами работы с приведенным в посте списком TList — приглашаю в этот пост моего блога.

5 1 голос
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
6 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Гость
Гость
25/04/2010 08:48

Что-то я не совсем понял как работает. А можно полный исходник? А то я начал делать, у меня некоторые переменные не видит почему-то.

Гость
Гость
25/04/2010 09:04

Спасибо, буду ждать)

OlegSkal
OlegSkal
09/07/2010 17:09

Выложите пожалуйста исходники или (что еще лучше) скиньте на
SOV.develop.ua@mail.ru

Alexander
Alexander
28/08/2015 00:16

С вашего разрешения, адаптировал код для FireMonkey procedure TForm1.Recurse; var iNode: IXMLNode; procedure ProcessNode(Node: IXMLNode; TreeNode: TTreeViewItem); var cNode: IXMLNode; TreeViewItem: TTreeViewItem; s: string; begin if Node = nil then Exit; TreeViewItem := TTreeViewItem.Create(TreeView1); if TreeNode=Nil then TreeView1.AddObject(TreeViewItem) else TreeNode.AddObject(TreeViewItem); if not (Node.ChildNodes.Count>0) and not (VarIsNull(Node.NodeValue)) then //проверка узла на вложенность в него подузлов и наличия значений TreeViewItem.Text := Node.NodeName + ‘ = ‘+ VarToStr(Node.NodeValue) else TreeViewItem.Text := Node.NodeName ; cNode := Node.ChildNodes.First; TreeNode := TreeViewItem; while cNode nil do begin ProcessNode(cNode, TreeNode); cNode := cNode.NextSibling; end; end; begin iNode := XMLDocument1.DocumentElement.ChildNodes.First;//стартуем с первого элемента while iNode nil do begin… Подробнее »