Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Bob Swart (aka Dr.Bob) - Medical Officer TDMWeb Kylix Developer's Guide
 Kylix 2+ BizSnap - XML, SOAP & WebServices (1 of 6)
See Also: Kylix Papers and Columns

I have been working on an BizSnap chapter for the Kylix Developer's Guide - adding more coverage of Kylix 2 and 3 to this book. The BizSnap chapter will be published on my website (in six weekly parts), covering XML Document Programming and Web Services support.


BizSnap is the name given by Borland to describe the feature set of Kylix 2+ Enterprise that covers XML document programming as well as Web Services (using SOAP). In this chapter, we will first cover XML document programming using the TXMLDocument component. This will be enhanced using XML Data Binding and even further using the XML Mapping Tool.
We then move on to Web Services, and after a short introduction to SOAP, we'll implement our first Web Service in Kylix 2+, followed by the usage (consuming) of Web Services written in Kylix or other development environments. Finally, we show the powerful combination of BizSnap with DataSnap to produce distributed applications (even cross-platform, considering that Delphi 6 also supports this on Windows).

1. XML Document Programming
Half of this chapter will cover XML document programming. The place to start this coverage (this section) is with the TXMLDocument component, which can be found on the Internet tab of the Kylix 2+ Component Palette. But before you can actually start to use this component, you first need an XML document to work with. While writing this Kylix 2+ specific chapter for the Kylix Developer's Guide, I decided to write my own XML document, which reflects the structure of this chapter (so it also helped me to focus on the topics to write about). The XML document that will be used throughout the entire chapter is stored in BizSnap.xml and is defined as follows:

  <?xml version="1.0" standalone='yes' ?>
  <Chapter Title="BizSnap">
   <Section Title="XML Document Programming">
    <Components>TXMLDocument</Components>
    <Wizards/>
   </Section>
   <Section Title="XML Data Binding">
    <Components>TXMLDocument</Components>
    <Wizards>XML Data Binding Wizard</Wizards>
   </Section>
   <Section Title="XML Mapping Tool">
    <Components>TXMLDocument, TXMLTransform, TXMLTransformProvider, TXMLTransformClient</Components>
    <Wizards>XML Mapper</Wizards>
   </Section>
   <Section Title="Web Service (Server)">
    <Components>THTTPSoapDispatcher, THTTPSoapPascalInvoker, TWSDLHTMLPublish</Components>
    <Wizards>Soap Server Application Wizard</Wizards>
   </Section>
   <Section Title="Consuming Web Services">
    <Components>THTTPRIO</Components>
    <Wizards>Web Services Importer</Wizards>
   </Section>
   <Section Title="Web Services and DataSnap">
    <Components>TSoapConnection</Components>
    <Wizards>Soap Server Data Module</Wizards>
   </Section>
  </Chapter>
As you can see, the chapter is divided into six sections, and each section covers certain components as well as wizards, all found in Kylix 2+ Enterprise. You are currently in the first section, which introduces XML document programming, using the above XML document and, as you can see, the TXMLDocument component. This component can be found on the Internet tab of Kylix 2+ Enterprise. If you drop it on a form, there are a number of important properties to examine.

XMLDocument Properties
First you have the DOMVendor property, which is set to Open XML by default. Apart from this DOM, you are free to install/register another DOM to be used by the TXMLDocument component. You can use the global variable DOMVendors for this task (refer to the online help for more information about using other DOM vendors).
The FileName property should point to the XML document that you want to work with. If the XML document is not stored in an external file, but rather received directly as a stream of XML data, you can use the XML property (note that these properties are mutual exclusive: if you specify a value for one, the other is cleared). The property editor for the XML property consists of a String list editor where you can type (or paste) the XML directly. For your example, use the BizSnap.xml file that is also available on the CD-ROM in the directory for this chapter (or on my Web site at http://www.drbob42.com/books/kylix).

Note: after you've selected the Filename, the Object Inspector will prepend the path, making it a fully qualified filename. This is nice, but a potential problem if you plan to move your project (or just the .XML document) to another location (for example when you load the example project from the CD-ROM onto your own machine).
Personally, I always modify the FileName property to make sure it only holds a relative filename, such as ../xml/BizSnap.xml or just BizSnap.xml (to use an XML document that must be in the same directory as the executable itself). The latter (a single filename) will also help to produce a cross-platform application, without worries about slashes and backslashes.

The NodeIndentStr property specifies the indentation level of the nodes in the XML tree. Anything between one and eight spaces or a tab can be used (although it'll only work correctly for two spaces or more - not for one space or a tab at this time). You are free to enter your own indentation string, such as 12 spaces. The value of this property is used if and only if the doNodeAutoIndent flag is set in the Options property. By default, this property is set to False, so the value of NodeIndentStr is ignored.
Apart from the doNodeAutoIndent flag, the Options property contains flags for doNodeAutoCreate, doAttrNull, doAutoPrefix, doNamespaceDecl, and doAutoSave. The last flag, which is also set to False by default, is used to automatically save the contents of the XML document in the location specified by the FileName or inside the XML property (depending on which one is used), whenever the TXMLDocument component is deactivated. If you want to explicitly save the contents of the XML document, you can always call the SaveToFile method, which has an optional FileName argument (if you omit this argument, it will use the value of the FileName property). Let's set the doAutoSave property to True in your example.
Next are the ParseOption flags, consisting of poResolveExternals, poValidateOnParse, poPreserveWhiteSpace, and poAsyncLoad. All are set to False by default, and won't be used in this example. (d)XMLDocument Interfaces The TXMLDocument component implements two different interfaces, although one is only available through a property. To start with the latter, the DOMDocument property of the TXMLDocument component implements the IDOMDocument interface, which is a low-level Document Object Model (DOM) interface definition, a standard in itself. The DOM consists of a tree-based API (compared to SAX, which is an event-based API). The unit xmldom.pas contains the definitions for IDOMDocument, IDOMNode, IDOMNodeList, IDOMAttr, IDOMElement, and IDOMText for this purpose.
Apart from the standard DOM interface, the TXMLDocument component also directly implements the IXMLDocument interface, a more high-level approach to working with XML documents and data. This is still a somewhat DOM-like interface, but a bit more powerful and easier to use, as I'll show in this section. The interfaces IXMLDocument, IXMLNode, IXMLNodeList, and IXMLNodeCollection are defined in the XMLIntf.pas unit. Although IDOMDocument is also available through the DOMDocument property, the IXMLDocument is easier and the preferred way (in Kylix 2+) to work with the TXMLDocument component, so let's examine the IXMLDocument interface in some more detail in this section. (d)Reading XML Documents Let's summarize the properties you've set in the TXMLDocument component. You've set the FileName to BizSnap.xml, and set the doAutoSave flag of the Options property to True. Now, you can open the TXMLDocument by setting the Active property to True as well. After the TXMLDocument is active, you can access the root and traverse through the hierarchy of nodes, reading and writing values, adding nodes, and more. Each node in the hierarchy is of type IXMLNode.
You can walk the hierarchy and use a TMemo component to display the nodes and their attributes you encounter along the way. First of all, the root node can be obtained using the DocumentElement property. After you have the root, you can get the attributes as well as child nodes. The following code will get the root node, the attribute with name Title, followed by the first child node with attributes Title, and child nodes Components and Wizards.

  procedure TForm1.Button1Click(Sender: TObject);
  var
    Chapter: IXMLNode; // Root Node
    Section: IXMLNode;
  begin
    Memo1.Lines.Clear;
    Chapter := XMLDocument1.DocumentElement;
    try
      Memo1.Lines.Add('Chapter: '+Chapter.Attributes['Title']);
      Section := Chapter.ChildNodes[1];
      Memo1.Lines.Add('Section: '+Section.Attributes['Title']);
      Memo1.Lines.Add('Components: '+Section.ChildNodes['Components'].Text);
      Memo1.Lines.Add('Wizards: '+Section.ChildNodes['Wizards'].Text);
    except
    end
  end;
Note that you have to make sure to give the exact names of the attributes and child nodes. If you supply an incorrect name for an attribute, for example, you get an exception of class EVariantError telling you that an Invalid variant type conversion was attempted. An incorrect name for a child node results in an empty node, and hence no value for the Text property. For each node, you can check the HasChildNodes property to make sure it indeed contains any child nodes. The ChildNodes.Count property contains the number of child nodes, just as the AttributeNodes.Count contains the number of attributes. And each node can return its Text as well as XML representation.
Using the available properties and methods in the IXMLNode and IXMLNodeList interfaces, you can write a method that can "dump" an XML document into a plain ASCII tree representation (one that can be loaded back into a TTreeview component). The following function TreeView implements this behavior:
  unit XmlTree;
  interface
  uses
    Variants, XMLIntf;

  function TreeView(XmlNode: IXMLNode; depth: Integer): AnsiString;

  implementation
  uses
    Tabs;

  function TreeView(XmlNode: IXMLNode; depth: Integer): AnsiString;
  var
    i: Integer;
    Attr: IXMLNode;
  begin
    Result := '';
    if XMLNode.NodeType = ntElement then
    begin
      if XmlNode.IsTextElement then
        if XmlNode.NodeValue <> null then
          Result := Tab[depth] +
            XmlNode.NodeName + ' = ' + XmlNode.NodeValue + CRLF
        else
          Result := Tab[depth] + XmlNode.NodeName + CRLF
      else
        if XmlNode.HasChildNodes then
          Result := Tab[depth] + '*' + XmlNode.NodeName + CRLF;

      for i:=0 to Pred(XmlNode.AttributeNodes.Count) do
      begin
        Attr := XmlNode.AttributeNodes[i];
        Result := Result + Tab[depth+1] +
          '[' + Attr.NodeName + ' = ' + Attr.NodeValue + ']' + CRLF
      end;
      if XmlNode.HasChildNodes then
        for i:=0 to Pred(XmlNode.ChildNodes.Count) do
          Result := Result + TreeView(XmlNode.ChildNodes[i], depth+1)
    end
  end {TreeView};

  end.
The unit Tabs is defined as follows (and is only used to produce the arrays of Tabs used for indentation of the output):
  unit Tabs;
  interface
  const
    CRLF: AnsiString = #13#10;
  const
    MaxTab = 254;
  var
    Tab: Array[0..MaxTab] of PChar;

  implementation
  var
    i: Integer;
    TabStr: ShortString;
  begin
    FillChar(TabStr,Sizeof(TabStr),#9);
    TabStr[0] := #255;
    TabStr[255] := #0;
    for i:=0 to 254 do
      Tab[i] := @TabStr[MaxTab-i+1];
  end.
Once the units XmlTree and Tab are available, you can call TreeView as follows, dumping the results in a file first, and then loading it in a TTreeview component:
  procedure TForm1.Button1Click(Sender: TObject);
  var
    TreeFile: TextFile;
  begin
    AssignFile(TreeFile,'XMLTree.log');
    Rewrite(TreeFile);
    write(TreeFile,Treeview(XMLDocument1.DocumentElement,0));
    CloseFile(TreeFile);
    Treeview1.LoadFromFile('XMLTree.log');
  end;
The result can be seen in Figure 1 below:

Figure 1: XML tree output in TTreeview component

Note: The Kylix 2+ CLX TTreeview component has a small bug that prevents it from indenting the tree correctly when using spaces inside the file (loaded by LoadFromFile). As a consequence, the Tabs unit is using Tabs (instead of spaces) to indent the nodes in the tree dump. (d)Writing XML Documents Apart from browsing through an XML document, you can also modify and save the updated XML document. In fact, it's so easy to change the XML Document that sometimes you've already done it without even knowing it. Remember how I told you about the doAutoSave option; this will make sure the contents of the TXMLDocument component is automatically saved whenever you made a change to it. Another option, the doNodeAutoCreate, will make sure that if you try to access a node that doesn't exist yet, it will dutifully create one for you. But the side effect of this all is that if you make one typing mistake (for example in the first listing), and don't search for the ChildNode['Wizards'] but the ChildNode['Wizard'] instead, then a new (empty) child node with name 'Wizard' will have been added to the XML Document.
Apart from changing the XML Document by accident, you can also use the IXMLDocument methods that are made for this, such as AddChild (which will explicitly add a child node) as well as SetAttribute, SetChildValue, SetNodeValue, and SetText.
If you take a look at the BizSnap.xml document after it has been updated by the TXMLDocument component, you'll note that it's using tabs to indent the old as well as new nodes.

Next Time, Dr.Bob says...
In the next section, we'll use the Data Binding Wizard to produce some more "specific" source code to perform XML document programming (with Code Insight support, among others). All this and more next week, so stay tuned...


This webpage © 2002-2009 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved.