Delphi XML Binding Wizard Tutorial

By: Andrea Raimondi

Abstract: The Delphi XML Binding Wizard requires some understanding to be used. I'll show you

    Introduction

The XML binding wizard lets developers using XML files avoid all the mess related to xml node management. This is obtained by abstracting the xml document to a set of interfaces defining the final values, rather than nodes.

    How the file is handled at the binding level

Each xml file is composed of a root node and several child nodes.

This means that the root node code will be an interface inheriting from XMLDocument.

While this may seem obvious to experienced users, beginners have to consider what this means, because it implies several other things.

First of all, it means that you can access the underlying original XML document if you have to perform some operations, such as an XPath selection.

Second, it implies that - if you really want to - you can reuse code relying on the IXMLDocument interface for your current binding. This is useful if you have, for example, routines to bind an XML document to a treeview (which is quite common).

Third, it means that it's very easy to generate a brand new XML file using the binding and then expose the generated XML, just use the XML property of type TStrings.

Child nodes are another consideration. The wizard can distinguish between composite and simple nodes. Thus, if you have a "items" node with several "item" nested nodes, the wizard will be smart enough to recognize the pattern and accomodate your generated code accordingly.

Child nodes can derive from any IXMLNode or inherited interface according to the type of node, except IXMLDocument (which is reserved, as mentioned above, to the root node).

You will notice that there's no function whatsoever to create a child node unless it is a composite node.

This is what the wizard is all about: you set interface properties and Delphi will take care of creating the nodes.

If, however, there's a composite node, there's then going to be an ADD method allowing you to set the child nodes. This method is a function returning the most convenient interface, like in this example:

var MyChildNode : IMyChildNode;
begin
  MyChildNode := MyParentNode.Add;
  // Set MyChildNode properties here, Delphi behind the scenes will create the nodes.
end;

    When to use the wizard

The wizard can be used in most occasions. It's very handy especially for those XML files that can get slightly convoluted or that are not under our control. The best feature of the wizard is that, if a new, compatible XML schema or file is sent to you, you can simply re-generate the source and you're done.

    Alternatives to the wizard

The main, supported, alternative to the wizard is the use of a client dataset.

While it's not exactly an XML file in its own merit (i.e. the CDS really is a database), it can be saved to XML and can also be transformed (using the XML mapper) into a custom XML file.

Thus, you can prototype your data model using a client dataset, transform it to an XML file, feed it to the wizard, and voilà.

    What to feed to the wizard

The binding wizard can be fed with several different xml file types:

  • Data files (with .xml extension)
  • Schema files (with .xsd extension)
  • XTR files (very rare, just don't care)

You'll likely feed the wizard with XML files, less likely (but still very possible) with schema ones.

    A sample file import

Let's see a sample XML file.

<?xml version="1.0" encoding="UTF-16"?>
<rootelement>
  <childnodes attr1="1">
      <childnode attr2="2">
         </childnode>
         <childnode attr2="2">
         </childnode>
         <childnode attr3="3">
         </childnode>
    </childnodes>
    <anotherchildnode>
      with text in it
    </anotherchildnode>
</rootelement>

Start the wizard and import it.

You will obtain these interfaces:

{ IXMLRootelementType }
IXMLRootelementType = interface(IXMLNode)
    ['{875F00D6-2822-4DC7-B4F7-6178F9456EC8}']
    { Property Accessors }
    function Get_Childnodes: IXMLChildnodesType;
    function Get_Anotherchildnode: WideString;
    procedure Set_Anotherchildnode(Value: WideString);
    { Methods & Properties }
    property Childnodes: IXMLChildnodesType read Get_Childnodes;
    property Anotherchildnode: WideString read Get_Anotherchildnode
      write Set_Anotherchildnode;
  end;

{ IXMLChildnodesType }

  IXMLChildnodesType = interface(IXMLNodeCollection)
    ['{F26112C9-A8F2-4852-8EE2-86E50702015B}']
    { Property Accessors }
    function Get_Attr1: Integer;
    function Get_Childnode(Index: Integer): IXMLChildnodeType;
    procedure Set_Attr1(Value: Integer);
    { Methods & Properties }
    function Add: IXMLChildnodeType;
    function Insert(const Index: Integer): IXMLChildnodeType;
    property Attr1: Integer read Get_Attr1 write Set_Attr1;
    property Childnode[Index: Integer]: IXMLChildnodeType 
      read Get_Childnode; default;
  end;

{ IXMLChildnodeType }

  IXMLChildnodeType = interface(IXMLNode)
    ['{E8DDAFCC-84BC-4067-808F-0D5DAF829436}']
    { Property Accessors }
    function Get_Attr2: Integer;
    function Get_Attr3: Integer;
    procedure Set_Attr2(Value: Integer);
    procedure Set_Attr3(Value: Integer);
    { Methods & Properties }
    property Attr2: Integer read Get_Attr2 write Set_Attr2;
    property Attr3: Integer read Get_Attr3 write Set_Attr3;
  end;

Now, if we look at this source code, we’ll see that, for instance, “AnotherChildNode” is a simple string, whereas ChildNodes has the famous .ADD method.


How do you use those interfaces? It’s pretty simple:

procedure NewXMLFile( out RootNode : IXMLRootelementType;AnotherChildElement : String );
begin
  RootNode := NewRootelement;
  RootNode.AnotherChildElement := AnotherChildElement;
end;

procedure AddChildElement( RootNode: IXMLRootElementType;Attr2, Attr3 : String );
var ChildNode : IXMLChildNodeType;
begin
   ChildNode := RootNode.ChildNodes.Add;
   ChildNode.Attr2 := Attr2;
   ChildNode.Attr3 := Attr3;
end;

Let’s now have a look at how the “NewRootElement” function is implemented:

function Newrootelement: IXMLRootelementType;
begin
  Result := NewXMLDocument.GetDocBinding('rootelement', TXMLRootelementType) as IXMLRootelementType;
end;

The “NewXMLDocument” function is declared in XMLDoc.pas and creates an interfaced TXMLDocument instance.

    When not to use the XML binding wizard?

As you probably noticed, the XML binding requires a DOM-oriented parser. That is, you need to be loading and keeping all of your document in memory.

Thus, you shouldn’t be using it when you don’t need all of your document in memory. An example of this may be, for instance, some kind of tag based generator. Consider this XML file:

<?xml version="1.0"?>
<build>
  <taskdefs>
    <taskdef name="DelphiCompiler" ExeFile="Dcc.exe"/>
  </taskdefs>
  <tasks>
     <task taskdef="DelphiCompiler" file="MyConsoleApp.dpr"/>
  </tasks>
</build>

In this case, you simply need to parse each tag, you don’t need a DOM, thus you don’t need a bound XML. Yeah, you can still choose to use one, but this is not required.

Another case is when you need XPath, then you got to use the native IXMLDocument interface. But that’s for another tutorial, really.

    Conclusions

As you can see, XML binding is quite easy to use and powerful. I’m sure you’ll like it quite a bit, actually, when you’re going to use it.

Now you know the basics to use it proficiently, just go and bind your files!

Server Response from: ETNASC04