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 (2 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 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).

2. XML Data Binding
In the previous section you worked with the IXMLDocument interface as implemented by the TXMLDocument component. And although it's useful, the downside is that is doesn't offer you semantic support for the XML document. One typing mistake, and you didn't find the value of a child node, but accidentally added a new child node to the tree.
Fortunately, Kylix 2 also contains a way to perform XML Data Binding, whereby an XML Document is used to generate Object Pascal source code. The generated source code contains interfaces and class definitions that will help you make less mistakes by offering named methods as well as Code Insight support. Let's see how it works, and you'll get the idea.
The "work" is done by the XML Data Binding Wizard, which can be found in the Object Repository after you choose File | New (see Figure 2).

Figure 2: XML Data Binding icon in the Object Repository

The XML Data Binding Wizard has three pages, although the first page is optional (as I'll explain in a moment). The first page is used to specify the name of the XML document (see Figure 3).

Figure 3: First page of the Data Binding Wizard

However, instead of starting the XML Data Binding Wizard from the Object Repository, you can also start it by double-clicking on the TXMLDocument component. This will make sure the XML Data Binding Wizard is already "loaded" with the XML Document, and you'll move to the second page already, which can be seen in Figure 4.

Figure 4: Second page of the Data Binding Wizard

On this second page of the XML Data Binding Wizard you get an overview that shows how the wizard will represent each XML element, and which Object Pascal code will be generated. As you can see in Figure 4, there are a number of complex types (ChapterType, SectionType) as well as simple types (String) used by your XML document. Generally, this page doesn't require any intervention, and can be regarded as "information only."
Before you move on to the last page, however, let's click on the Options button to view the possible options you may want to change (see Figure 5).

Figure 5: Options page of the Data Binding Wizard

As you can see in Figure 5, you can change some of the code that will be generated, such as the Get_ and Set_ prefix for the getter and setter routines). The Data Type map is not useful for this example because you only use the String type, but it may be convenient when you want to use custom types at a later time.
When you're ready, close the XML Data Binding Wizard Options dialog, and click on the Next button to go to the third and last page of the XML Data Binding Wizard (see Figure 6).

Figure 6: Third page of the Data Binding Wizard

This third and last page of the XML Data Binding Wizard is only used as information again, and in your case displays two generated interfaces: IXMLChapterType and IXMLSectionType. For both types, you can preview the generated interface definition, for which the properties are most interesting. The IXMLChapterType contains a Title as well as Section (array) property to access the Title attribute and the Section child nodes. The IXMLSectionType will contain properties for Title, Components, and Wizards.
You can decide to store the binding settings, for later use, in the suggested file BizSnap.xdb (this file can be re-used at the first page of the Data Binding Wizard).
When you click the Finish button, the source code for the interfaces will be generated in a new unit that will be added to the current project. Save this new unit as BizSnap.pas (so you know where it belongs to). The contents should be as follows:
  {******************************************}
  {                                          }
  {             XML Data Binding             }
  {                                          }
  {         Generated on: 01/21/02 01:43:26  }
  {       Generated from: BizSnap.xml        }
  {                                          }
  {******************************************}
  unit BizSnap;
  interface
  uses
    xmldom, XMLDoc, XMLIntf;

  type

  { Forward Decls }

    IXMLChapterType = interface;
    IXMLSectionType = interface;

  { IXMLChapterType }

    IXMLChapterType = interface(IXMLNodeCollection)
      ['{8C1BC176-6C0E-D611-87C6-68D6D308EEFE}']
      { Property Accessors }
      function Get_Title: WideString;
      function Get_Section(Index: Integer): IXMLSectionType;
      procedure Set_Title(Value: WideString);
      { Methods & Properties }
      function Add: IXMLSectionType;
      function Insert(const Index: Integer): IXMLSectionType;
      property Title: WideString read Get_Title write Set_Title;
      property Section[Index: Integer]: IXMLSectionType read Get_Section; default;
    end;

  { IXMLSectionType }

    IXMLSectionType = interface(IXMLNode)
      ['{A42FC276-6C0E-D611-87C6-0072EC08EEFE}']
      { Property Accessors }
      function Get_Title: WideString;
      function Get_Components: WideString;
      function Get_Wizards: WideString;
      procedure Set_Title(Value: WideString);
      procedure Set_Components(Value: WideString);
      procedure Set_Wizards(Value: WideString);
      { Methods & Properties }
      property Title: WideString read Get_Title write Set_Title;
      property Components: WideString read Get_Components write Set_Components;
      property Wizards: WideString read Get_Wizards write Set_Wizards;
    end;

  { Forward Decls }

    TXMLChapterType = class;
    TXMLSectionType = class;

  { TXMLChapterType }

    TXMLChapterType = class(TXMLNodeCollection, IXMLChapterType)
    protected
      { IXMLChapterType }
      function Get_Title: WideString;
      function Get_Section(Index: Integer): IXMLSectionType;
      procedure Set_Title(Value: WideString);
      function Add: IXMLSectionType;
      function Insert(const Index: Integer): IXMLSectionType;
    public
      procedure AfterConstruction; override;
    end;

  { TXMLSectionType }

    TXMLSectionType = class(TXMLNode, IXMLSectionType)
    protected
      { IXMLSectionType }
      function Get_Title: WideString;
      function Get_Components: WideString;
      function Get_Wizards: WideString;
      procedure Set_Title(Value: WideString);
      procedure Set_Components(Value: WideString);
      procedure Set_Wizards(Value: WideString);
    end;

  { Global Functions }

  function GetChapter(Doc: IXMLDocument): IXMLChapterType;
  function LoadChapter(const FileName: WideString): IXMLChapterType;
  function NewChapter: IXMLChapterType;

  implementation
  { Global Functions }

  function GetChapter(Doc: IXMLDocument): IXMLChapterType;
  begin
    Result := Doc.GetDocBinding('Chapter', TXMLChapterType) as IXMLChapterType;
  end;

  function LoadChapter(const FileName: WideString): IXMLChapterType;
  begin
    Result := LoadXMLDocument(FileName).GetDocBinding('Chapter', TXMLChapterType) as IXMLChapterType;
  end;

  function NewChapter: IXMLChapterType;
  begin
    Result := NewXMLDocument.GetDocBinding('Chapter', TXMLChapterType) as IXMLChapterType;
  end;

  { TXMLChapterType }

  procedure TXMLChapterType.AfterConstruction;
  begin
    RegisterChildNode('Section', TXMLSectionType);
    ItemTag := 'Section';
    ItemInterface := IXMLSectionType;
    inherited;
  end;

  function TXMLChapterType.Get_Title: WideString;
  begin
    Result := AttributeNodes['Title'].Text;
  end;

  procedure TXMLChapterType.Set_Title(Value: WideString);
  begin
    SetAttribute('Title', Value);
  end;

  function TXMLChapterType.Get_Section(Index: Integer): IXMLSectionType;
  begin
    Result := List[Index] as IXMLSectionType;
  end;

  function TXMLChapterType.Add: IXMLSectionType;
  begin
    Result := AddItem(-1) as IXMLSectionType;
  end;

  function TXMLChapterType.Insert(const Index: Integer): IXMLSectionType;
  begin
    Result := AddItem(Index) as IXMLSectionType;
  end;

  { TXMLSectionType }

  function TXMLSectionType.Get_Title: WideString;
  begin
    Result := AttributeNodes['Title'].Text;
  end;

  procedure TXMLSectionType.Set_Title(Value: WideString);
  begin
    SetAttribute('Title', Value);
  end;

  function TXMLSectionType.Get_Components: WideString;
  begin
    Result := ChildNodes['Components'].Text;
  end;

  procedure TXMLSectionType.Set_Components(Value: WideString);
  begin
    ChildNodes['Components'].NodeValue := Value;
  end;

  function TXMLSectionType.Get_Wizards: WideString;
  begin
    Result := ChildNodes['Wizards'].Text;
  end;

  procedure TXMLSectionType.Set_Wizards(Value: WideString);
  begin
    ChildNodes['Wizards'].NodeValue := Value;
  end;

  end.
As you can see from this listing, there are two new interface types (the IXMLChapterType and IXMLSectionType which you saw earlier in the last page of the XML Data Binding Wizard). But what's even more useful is the fact that the new unit also contains the class definitions and implementations for TXMLChapterType and TXMLSectionType. TXMLChapterType is derived from TXMLNodeCollection (it contains child nodes), and TXMLSectionType is derived from TXMLNode. Both implement their similar-named interface.
But that's not all, because the BizSnap.pas unit also contains three global functions: GetChapter, LoadChapter, and NewChapter. The first can be used to "extract" a chapter from an existing TXMLDocument component. The second can be used to load a chapter from an existing - compatible - XML file (such as the BizSnap.xml file), and the NewChapter function can be used to start a new, empty, chapter. All three return an IXMLChapterType interface, which can then be used to work with.
As an example, let's use the GetChapter function to extract the IXMLChapterType from the TXMLDocument component and work with it using the named methods and properties. First you must add the BizSnap unit to the uses clause of your mainform, so you can use the interface types and global functions. Then, to use the IXMLChapterType interface during the lifetime of your form, you can add a variable Chapter of type IXMLChapterType to the form (place it in the public interface section), and write the following code in the OnCreate event handler of the form:
  procedure TForm1.FormCreate(Sender: TObject);
  begin
    Chapter := GetChapter(XMLDocument1);
  end;
As you'll find out when you type along, the Code Insight features of the Kylix 2 IDE will now help you when writing code. Specifically, if you type GetChapter, you will be helped with the argument. And when you want to use the chapter (of type IXMLChapterType), then Code Insight will show you a list of properties and methods. No more accidental typing mistakes resulting in modified XML documents with nonsense child nodes.
Now, let's add two TButton components, call them btnPrev and btnNext, and write the following code for them (to display information from the previous and next section).
  procedure TForm1.FormCreate(Sender: TObject);
  begin
    Chapter := GetChapter(XMLDocument1);
    CurrentSection := 0; // first section
    btnNextClick(nil)
  end;

  procedure TForm1.btnPrevClick(Sender: TObject);
  begin
    if CurrentSection > 0 then Dec(CurrentSection);
    Memo1.Lines.Clear;
    Memo1.Lines.Add(Chapter.Section[CurrentSection].Title);
    Memo1.Lines.Add(Chapter.Section[CurrentSection].Components);
    Memo1.Lines.Add(Chapter.Section[CurrentSection].Wizards);
    btnNext.Enabled := (CurrentSection < Pred(Chapter.Count));
    btnPrev.Enabled := (CurrentSection > 0)
  end;

  procedure TForm1.btnNextClick(Sender: TObject);
  begin
    if CurrentSection < Pred(Chapter.Count) then Inc(CurrentSection);
    Memo1.Lines.Clear;
    Memo1.Lines.Add(Chapter.Section[CurrentSection].Title);
    Memo1.Lines.Add(Chapter.Section[CurrentSection].Components);
    Memo1.Lines.Add(Chapter.Section[CurrentSection].Wizards);
    btnNext.Enabled := (CurrentSection < Pred(Chapter.Count));
    btnPrev.Enabled := (CurrentSection > 0)
  end;
You can now view the contents of one item of the XML document inside the TMemo control, and use the Next and Prev buttons to "navigate" through the XML document.

Next Time, Dr.Bob says...
In the next section, we'll use the XML Mapping Tool to produce source code that can transform an XML document to a DataSet (and back) for even more powerful functionality. 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.