Так как в последнее время очень много работаю с 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
На данный момент с использованием класса можно сделать следующее:
- Загрузить XML-документ и построить дерево TreeView
- Получить список всех узлов
- Получить целиком (вместе с атрибутами) узел XML-документа, которому соответствует узел TreeView c текстом cText
- Загрузить в 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 — приглашаю в этот пост моего блога.
Что-то я не совсем понял как работает. А можно полный исходник? А то я начал делать, у меня некоторые переменные не видит почему-то.
Можно, как раз собирался оформить полноценный пример и выложить. Наверное сегодня или завтра распишу всё подробненько и выложу все исходники
Спасибо, буду ждать)
Выложите пожалуйста исходники или (что еще лучше) скиньте на
SOV.develop.ua@mail.ru
Исходник выложен давным давно — тут http://www.webdelphi.ru/2010/04/isxodnik-xml2treeview/
С вашего разрешения, адаптировал код для 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… Подробнее »