Abstracting XML Documents with the Data Binding Wizard

From RAD Studio
Jump to: navigation, search

Go Up to Working with XML Documents Index


It is possible to work with an XML document using only the TXMLDocument component and the IXMLNode interface it surfaces for the nodes in that document, or even to work exclusively with the DOM interfaces (avoiding even TXMLDocument). However, you can write code that is much simpler and more readable by using the XML Data Binding wizard.

The Data Binding wizard takes an XML schema or data file and generates a set of interfaces that map on top of it. For example, given XML data that looks like the following:

 <customer id=1>
   <name>Mark</name>
   <phone>(831) 431-1000</phone>
 </customer>

The Data Binding wizard generates the following interface (along with a class to implement it):

Delphi:

ICustomer = interface(IXMLNode)
       ['{8CD6A6E8-24FC-426F-9718-455F0C507C8E}']
       { Property Accessors }
       function Get_Id: Integer;
       function Get_Name: WideString;
       function Get_Phone: WideString;
       procedure Set_Id(Value: Integer);
       procedure Set_Name(Value: WideString);
       procedure Set_Phone(Value: WideString);
       { Methods & Properties }
       property Id: Integer read Get_Id write Set_Id;
       property Name: WideString read Get_Name write Set_Name;
       property Phone: WideString read Get_Phone write Set_Phone;
end;

C++:

__interface INTERFACE_UUID("{F3729105-3DD0-1234-80e0-22A04FE7B451}") ICustomer :
    public IXMLNode
{
public:
  virtual int __fastcall Getid(void) = 0 ;
  virtual DOMString __fastcall Getname(void) = 0 ;
  virtual DOMString __fastcall Getphone(void) = 0 ;
  virtual void __fastcall Setid(int Value)= 0 ;
  virtual void __fastcall Setname(DOMString Value)= 0 ;
  virtual void __fastcall Setphone(DOMString Value)= 0 ;
  __property int id = {read=Getid, write=Setid};
  __property DOMString name = {read=Getname, write=Setname};
  __property DOMString phone = {read=Getphone, write=Setphone};
};

Every child node is mapped to a property whose name matches the tag name of the child node and whose value is the interface of the child node (if the child is an internal node) or the value of the child node (for leaf nodes). Every node attribute is also mapped to a property, where the property name is the attribute name and the property value is the attribute value.

In addition to creating interfaces (and implementation classes) for each tagged element in the XML document, the wizard creates global functions for obtaining the interface to the root node. For example, if the XML above came from a document whose root node had the tag <Customers>, the Data Binding wizard would create the following global routines:

Delphi:

function Getcustomers(Doc: IXMLDocument): IXMLCustomerType;
function Loadcustomers(const FileName: WideString): IXMLCustomerType;
function Newcustomers: IXMLCustomerType;

C++:

extern PACKAGE _di_ICustomers __fastcall GetCustomers(TXMLDocument *XMLDoc);
extern PACKAGE _di_ICustomers __fastcall GetCustomers(_di_IXMLDocument XMLDoc);
extern PACKAGE _di_ICustomers __fastcall LoadCustomers(const WideString FileName);
extern PACKAGE _di_ICustomers __fastcall NewCustomers(void);

The Get... function takes the interface for a TXMLDocument instance . The Load... function dynamically creates a TXMLDocument instance and loads the specified XML file as its value before returning an interface pointer. The New... function creates a new (empty) TXMLDocument instance and returns the interface to the root node.

Using the generated interfaces simplifies your code, because they reflect the structure of the XML document more directly. For example, instead of writing code such as the following:

Delphi:

CustIntf := XMLDocument1.DocumentElement;
CustName := CustIntf.ChildNodes[0].ChildNodes['name'].Value;

C++:

_di_IXMLNode CustIntf = XMLDocument1->DocumentElement;
CustName = CustIntf->ChildNodes->Nodes->GetNode(0)->ChildNodes->Nodes[WideString("name")]->Value;

Your code would look as follows:

Delphi:

CustIntf := GetCustomers(XMLDocument1);
CustName := CustIntf[0].Name;

C++:

_di_ICustomers CustIntf = GetCustomers(XMLDocument1);
CustName = CustIntf->Nodes->GetNode(0)->Name;

Note that the interfaces generated by the Data Binding wizard all descend from IXMLNode. This means you can still add and delete child nodes in the same way as when you do not use the Data Binding wizard. (See the Adding and deleting child nodes section of Working with XML Nodes.) In addition, when child nodes represent repeating elements (when all of the children of a node are of the same type), the parent node is given two methods, Add, and Insert, for adding additional repeats. These methods are simpler than using AddChild, because you do not need to specify the type of node to create.

The following topics provide detailed information on using the XML Data Binding wizard:

See Also