Creating and Traversing XML Documents

Copyright (c) 2000 by Charlie Calvert

 

This paper shows how to iterate through an XML document.

Creating an XML Document

var
  FDoc: TDOMDocument;
  FRoot: TDomElement;
begin
  FDoc := TDomDocument.Create;
  // Create XML root
  FRoot := Fdoc.createElement('RootElement');
  FDoc.appendChild(FRoot);
end;

Appending Top Level Children

var
  FChild11, FChild12, FChild13: TDomElement;
begin
  FChild11 := Fdoc.createElement('Child_One');
  FRoot.appendChild(FChild11);
  FChild12 := Fdoc.createElement('Child_Two');
  FRoot.appendChild(FChild12);
  FChild13 := Fdoc.createElement('Child_Three');
  FRoot.appendChild(FChild13);

Adding GrandChild Nodes

var
  FGrandChild_111: TDomElement;
begin
  FGrandChild_111 := FDoc.createElement('GrandChild_111');
  FChild11.appendChild(FGrandChild_111);

Adding Other Types of Nodes

var
  attr01, Attr02: TDomAttr;
  textNode1, textNode2: TDomText;
begin
  attr02 := Fdoc.createAttribute('Attr01');
  FChild12.setAttributeNode(attr02);
  textNode2 := Fdoc.createText('Text Two');
  FChild12.appendChild(textNode2);

Working with Text Nodes

    NewElement := FDoc.CreateElement('Foo');
    TextNode := FDoc.CreateText(#13#10#32#32);
    Game.AppendChild(TextNode);
    NewElement.AppendChild(FDoc.CreateText('Foo Text'));
    Game.AppendChild(NewElement);
    TextNode := FDoc.CreateText(#13#10);
    Game.AppendChild(TextNode);

Node Iteration

Node Filters

type

  TMyDomNodeFilter = class(TDomNodeFilter)
    FNodeType: TDOMWhatToShow;
    FReject: Boolean;
  public
    constructor Create(nodeType: TDomWhatToShow; reject: Boolean=false);
    function acceptNode(const node: TdomNode): TdomFilterResult; override;
  end;

implementation

// ... Code ommitted here

function TMyDomNodeFilter.acceptNode(const node: TdomNode): TdomFilterResult;
begin
  if (Node.NodeType = TDomNodeType(0)) then
    Result := FILTER_ACCEPT;
  if node.NodeType in fNodeType then begin
     Result := FILTER_Accept;
  end else begin
    if FReject then
      Result := FILTER_REJECT
    else
      Result := FILTER_SKIP;
  end;
end;

Iterating Nodes

var
  ANode: TDomNode;
  Iter: TDomNodeIterator;
  WhatToShow: TDomWhatToShow;
  Filter: TMyDomNodeFilter;

begin
  whatToShow := SHOW_ALL;
  Filter := TMyDomNodeFilter.Create(SHOW_ALL);
  
  iter := FDoc.createNodeIterator(FRoot, whatToShow,  filter, true);
  Assert(iter.WhatToShow = SHOW_ALL);
  //ASSERT(iter.ExpandEntityReferences = True);

  ANode := iter.nextNode();
  ASSERT (ANode = FRoot);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT (ANode = FChild11);
  ListBox1.Items.Add(ANode.Code);
 
  ... // Code ommitted here
  
  ANode := iter.previousNode();
  ASSERT(ANode = FGrandChild_131);
  ListBox1.Items.Add(ANode.Code);

end;

Listing 1: Code for creating and traversing an XML document.

unit Main;

interface

{$ASSERTIONS ON}

uses
  Windows, Messages, SysUtils,
  Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls,
  OleServer, XDOM, ExtCtrls;

type
  TForm1 = class(TForm)
    DomImplementation1: TDomImplementation;
    XmlToDomParser1: TXmlToDomParser;
    ListBox1: TListBox;
    Panel1: TPanel;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    FDoc: TDOMDocument;
    FRoot: TDomElement;
    FChild11, FChild12, FChild13: TDomElement;
    FGrandChild_111, FGrandChild_112: TDomElement;
    FGrandChild_121, FGrandChild_122: TDomElement;
    FGrandChild_131: TDOMElement;
    attr01, Attr02: TDomAttr;
    textNode1, textNode2: TDomText;
    Comment: TDomComment;
    cDataSec: TDOMCDataSection;
    docPi: TDOMProcessingInstruction;
    procedure Traverse;
    procedure CreateDocument;
  public
    { Public declarations }
  end;

  TMyDomNodeFilter = class(TDomNodeFilter)
    FNodeType: TDOMWhatToShow;
    FReject: Boolean;
  public
    constructor Create(nodeType: TDomWhatToShow; reject: Boolean=false);
    function acceptNode(const node: TdomNode): TdomFilterResult; override;
  end;


var
  Form1: TForm1;

implementation

uses
  CodeBox, ComObj;

{$R *.DFM}

procedure TForm1.CreateDocument;
var
  Stream: TFileStream;
  S: string;
begin
  FDoc := TDomDocument.Create;
  // Create XML root
  FRoot := Fdoc.createElement('RootElement');
  FDoc.appendChild(FRoot);
  // Create Top Tier Children
  FChild11 := Fdoc.createElement('Child_One');
  FRoot.appendChild(FChild11);
  FChild12 := Fdoc.createElement('Child_Two');
  FRoot.appendChild(FChild12);
  FChild13 := Fdoc.createElement('Child_Three');
  FRoot.appendChild(FChild13);

  
  // Attach some text
  textNode1 := Fdoc.createText('Text One');
  FChild11.appendChild(textNode1);
  textNode2 := Fdoc.createText('Text Two');
  FChild12.appendChild(textNode2);

  //Create GrandChildren
  FGrandChild_111 := FDoc.CreateElement('GrandChild_111');
  FChild11.appendChild(FGrandChild_111);
  attr01  := Fdoc.CreateAttribute('Attr01');
  FChild11.setAttributeNode(attr01);

  FGrandChild_112 := Fdoc.createElement('GrandChild_112');
  FChild11.appendChild(FGrandChild_112);

  FGrandChild_121 := Fdoc.createElement('GrandChild_121');
  FChild12.appendChild(FGrandChild_121);
  attr02 := Fdoc.createAttribute('Attr01');
  FChild12.setAttributeNode(attr02);

  FGrandChild_122 := Fdoc.createElement('GrandChild_122');
  FChild12.appendChild(FGrandChild_122);

  FGrandChild_131 := FDoc.createElement('GrandChild_131');
  FChild13.appendChild(FGrandChild_131);

  Comment := FDoc.createComment('DocComment');
  FRoot.appendChild(Comment);

  cdataSec := Fdoc.createCDATASection('DocCDataSection');
  FChild11.appendChild(cdataSec);

  docPI := Fdoc.createProcessingInstruction('DocPI', 'DocTarget');
  FChild13.appendChild(docPI); 
  S := FDoc.Code;
  Stream := TFileStream.Create(GetStartDir +   'sammy.xml', fmCreate or fmOpenWrite);
  Stream.Write(PChar(S)^, Length(S));
  Stream.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  CreateDocument;
  Traverse;
end;

procedure TForm1.Traverse;
var
  ANode: TDomNode;
  Iter: TDomNodeIterator;
  WhatToShow: TDomWhatToShow;
  Filter: TMyDomNodeFilter;

begin
  whatToShow := SHOW_ALL;
  Filter := TMyDomNodeFilter.Create(SHOW_ALL);

  iter := FDoc.createNodeIterator(FRoot, whatToShow,  filter, true);
  Assert(iter.WhatToShow = SHOW_ALL);
  //ASSERT(iter.ExpandEntityReferences = True);

  ANode := iter.nextNode();
  ASSERT (ANode = FRoot);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT (ANode = FChild11);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = textNode1);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = FGrandChild_111);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = FGrandChild_112);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = cdataSec);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = FChild12);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = textNode2);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = FGrandChild_121);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = FGrandChild_122);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = FChild13);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = FGrandChild_131);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = docPI);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.nextNode();
  ASSERT(ANode = comment);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.previousNode();
  ASSERT(ANode = comment);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.previousNode();
  ASSERT(ANode = docPI);
  ListBox1.Items.Add(ANode.Code);

  ANode := iter.previousNode();
  ASSERT(ANode = FGrandChild_131);
  ListBox1.Items.Add(ANode.Code);
end;

{ TMyDomNodeFilter }

constructor TMyDomNodeFilter.Create(nodeType: TDomWhatToShow;
  reject: Boolean);
begin
  FReject := reject;
  FNodeType := NodeType;
end;

function TMyDomNodeFilter.acceptNode(const node: TdomNode): TdomFilterResult;
begin
  if (Node.NodeType = TDomNodeType(0)) then
    Result := FILTER_ACCEPT;
  if node.NodeType in fNodeType then begin
     Result := FILTER_ACCEPT;
  end else begin
    if FReject then
      Result := FILTER_REJECT
    else
      Result := FILTER_SKIP;
  end;
end;

end.