Delphi Clinic | C++Builder Gate | Training & Consultancy | Delphi Notes Weblog | Dr.Bob's Webshop |
|
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.
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).
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).
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.
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."
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.
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.
{******************************************} { } { 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.
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.
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...