2005. november 12., szombat
XML File Viewer
Problem/Question/Abstract:
How to create a simple XML File viewer, without worrying about the XML itself. The easiest solution is using the Microsoft XML Document Object Model, installed on every machine that run MS Internet Explorer 4 or higher.
Answer:
In this article I am showing you a simple XML file viewer, that may be extended to an XML editor with some work. You may enhance the editor by importing icons for the different node types, etc. That's up to you.
My main idea is to show you how to import type libraries from readily available components installed on nearly every machine in the modern windows world.
Note: The MS XML DOM is available for free download and redistribution at msdn.microsoft.com
IMPORTING THE MS XML TYPE LIBRARY
Start Delphi and create a new application, I you haven't done so already. In the Delphi menu go to Project|Import Type Library... A dialog will appear on your screen, with a list of all installed and registered COM libraries available for import. Take a moment and scroll through it, you might be surprised.
Somewhere down the list you find Microsoft XML, version 2.0 (Version 2.0). This is the type library we are going to import. Additionally, you may see Microsoft XML, v3.0 (Version 3.0). This is a newer and faster version from MS, we are going to use the older version however, since it is more common.
After selecting the MS XML, version 2.0 component object, select a Unit Directory, and press the Create Unit button. The Install button will install the component in your Component Pallete, additionally.
PREPARING YOUR APPLICATION FORM
Drop a MainMenu (Standard) component on your form, and insert a Open Menu item (name: Open1).
Drop a TreeView (Win32) component on your form, set Align=alLeft and ReadOnly=True (name: trvStructure).
Drop an OpenDialog (Dialogs) component on your form (name: OpenDialog1).
Drop a Panel (Standard) component on your form, set Align=alClient and clear the Caption (name: Panel1).
Drop a StringGrid (Additional) component on the Panel1 set Align=alTop, RowCount=2, ColCount=2, FixedCols=0, FixedRows=1 (name: grdAttributes).
Drop a Memo (Standard) on the Panel1 set Align=alClient, ReadOnly=True (name mmoNodeContent).
Note: The names I have used will appear in the source code again!
A PSEUDO CLASS FOR THE XML INTERFACES
Because mapping of interface via pointers introduces some problems I chose to create a simple class that contains only on variable holding the reference to the XML Node interface.
type
TXMLNodeWrapper = class
private
FNode: IXMLDOMNode;
protected
public
constructor Create(aNode: IXMLDOMNode);
property Node: IXMLDOMNode read FNode;
end;
The constructor will save the reference in the FNode variable.
CREATING THE XML DOM OBJECT
Creating an instance of the object is rather simple. Having a variable FDocument of the type IXMLDOMDocument, defined in the imported MSXML_TLB.
FDocument := CoDOMDocument.Create;
Next you need to set up the component to your needs.
FDocument.async := False;
FDocument.validateOnParse := True;
FDocument.preserveWhiteSpace := True;
The first I want to do is inserting an base element into the document. Every XML document needs at least this base element. I have named it xmlstart.
Note: Be careful, XML is case-sensitive.
FDocument.appendChild(FDocument.createNode(NODE_ELEMENT, 'xmlstart', ''));
PARSING THE XML DOCUMENT
There are quite many ways of parsing XML. I want to show you two recursive ways, that are very similar, but have quite different results.
(1) NodeList := Node.childNodes;
Returns all children, inlcude some special node types, such as #text or #comment. These node types require special care.
(2) NodeList := Node.selectNodes('*');
Returns all standard node types, that can be accessed via XSL (XML Structured Language). These node types are easy in use.
ACCESSING THE NODE LIST
Accessing any item in a Node List is very easy. The length returns the count of items in the list (equal to Delphis Count property). The Item array gives access to every Item of the node list.
for I := 0 to Pred(XMLNodeList.length) do
ShowMessage(XMLNodeList.item[I].nodeName);
MORE INFORMATION ABOUT THE MS XML DOM
The most important web addresses for the MS XML DOM are:
http://msdn.microsoft.com/xml (all about XML)
http://msdn.microsoft.com/downloads/default.asp?URL=/code/topic.asp?URL=/msdn-files/028/000/072/topic.xml (Downloads)
THE SOURCE CODE FOR THE XML VIEWER
unit uMainForm;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
MSXML_TLB, ComCtrls, Menus, Grids, ExtCtrls, StdCtrls;
type
TXMLNodeWrapper = class
private
FNode: IXMLDOMNode;
protected
public
constructor Create(aNode: IXMLDOMNode);
property Node: IXMLDOMNode read FNode;
end;
TfrmMain = class(TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
Open1: TMenuItem;
trvStructure: TTreeView;
OpenDialog1: TOpenDialog;
Panel1: TPanel;
grdAttributes: TStringGrid;
mmoNodeContent: TMemo;
procedure FormCreate(Sender: TObject);
procedure Open1Click(Sender: TObject);
procedure trvStructureChange(Sender: TObject; Node: TTreeNode);
private
FDocument: IXMLDOMDocument;
FFileName: string;
procedure LoadXML;
public
end;
var
frmMain: TfrmMain;
implementation
{$R *.DFM}
{ TXMLNodeWrapper }
constructor TXMLNodeWrapper.Create(aNode: IXMLDOMNode);
begin
inherited Create;
FNode := aNode;
end;
{ TFrmMain }
procedure TfrmMain.FormCreate(Sender: TObject);
begin
FDocument := CoDOMDocument.Create;
FDocument.async := False;
FDocument.validateOnParse := True;
FDocument.preserveWhiteSpace := True;
FDocument.appendChild(FDocument.createNode(NODE_ELEMENT, 'xmlstart', ''));
grdAttributes.Cells[0, 0] := 'Attribute name';
grdAttributes.Cells[1, 0] := 'Attribute value';
end;
procedure TfrmMain.LoadXML;
procedure EnterNode(const XMLNode: IXMLDOMNode; TreeNode: TTreeNode);
var
I: Integer;
XMLNodeList: IXMLDOMNodeList;
NewTreeNode: TTreeNode;
begin
NewTreeNode := trvStructure.Items.AddChild(TreeNode, XMLNode.nodeName);
NewTreeNode.Data := TXMLNodeWrapper.Create(XMLNode);
// use XMLNode.childNodes to get all nodes (incl. special types)
XMLNodeList := XMLNode.selectNodes('*');
for I := 0 to Pred(XMLNodeList.length) do
EnterNode(XMLNodeList.item[I], NewTreeNode);
end;
begin
for I := 0 to trvStructure.Items.Count - 1 do
TXMLNodeWrapper(trvStructure.Items.Item[I].Data).Destroy;
trvStructure.Items.BeginUpdate;
try
trvStructure.Items.Clear;
EnterNode(FDocument.documentElement, nil);
finally
trvStructure.Items.EndUpdate;
end;
end;
procedure TfrmMain.Open1Click(Sender: TObject);
begin
if OpenDialog1.Execute then
begin
FDocument.load(OpenDialog1.FileName);
FFileName := OpenDialog1.FileName;
LoadXML;
end;
end;
procedure TfrmMain.trvStructureChange(Sender: TObject; Node: TTreeNode);
var
I: Integer;
CurrentNode: IXMLDOMNode;
begin
CurrentNode := TXMLNodeWrapper(Node.Data).Node;
Caption := CurrentNode.nodeName;
if CurrentNode.selectNodes('*').length = 0 then
mmoNodeContent.Text := CurrentNode.text
else
mmoNodeContent.Text := '';
if CurrentNode.attributes.length > 0 then
begin
grdAttributes.RowCount := Succ(CurrentNode.attributes.length);
grdAttributes.FixedRows := 1;
for I := 0 to Pred(CurrentNode.attributes.length) do
begin
grdAttributes.Cells[0, Succ(I)] := CurrentNode.attributes.item[I].nodeName;
grdAttributes.Cells[1, Succ(I)] := CurrentNode.attributes.item[I].text;
end;
end
else
begin
grdAttributes.RowCount := 2;
grdAttributes.Cells[0, 1] := '';
grdAttributes.Cells[1, 1] := '';
end;
end;
end.
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése