XML Document Programming

   

The place to start the coverage of XML Document Programming in C++Builder 6 is the TXMLDocument component, which can be found on the Internet tab of the Component Palette. But, before we can actually start to use this component, we first need an actual XML document to work with. While writing this chapter for the C++Builder 6 Developer's Guide , I decided to write my own XML document, which reflects the structure of this chapter (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 defined as follows :rage of XML Document Programming in C++Builder 6 is the

[View full width]
 
[View full width]
<?xml version="1.0" standalone='yes' ?> <Chapter Title="XML Document Programming and XML Mapper"> <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, TXMLTransform Client</ graphics/ccc.gif Components> <Wizards>XML Mapper</Wizards> </Section> </Chapter>

As you can see, the chapter is divided into three sections. Each section covers certain components as well as, optionally wizards, which are all found in C++Builder 6 Enterprise (although in the appendix we will show how you can use the TXMLDocument component also in the Professional version of C++Builder 6). We are currently in the first section, which introduces XML document programming, using the previous XML document and, as you can see, the TXMLDocument component. This component can be found on the Internet tab of C++Builder 6 Enterprise (or Professional ”if you've followed the steps in the appendix). If you drop it on a form, there are a number of important properties to examine.

XML Document Properties

First, we have the DOMVendor property, which is set to MSXML by default, but can also be set to Open XML (at least on my machine). Apart from these two values for DOMVendor , you are free to install and register other DOMs to be used by the TXMLDocument component, such as one found in TurboPower's XML Partner. You can use the global variable DOMVendors for this task (refer to the online help for more information about installing, registering, and using other DOM Vendors).

The FileName property should point to the XML document that we 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 mutually 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 our example, let's 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/BizSnap.xml).

NOTE

Note that 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 or deploy the final application to another 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 later (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. 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 , 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 FileName or 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 our 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 chapter.

XML Document 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 Delphi interface (which gets typecast into C++Builder _di_IDOMDocument interface), a low-level Document Object Model (DOM) interface definition. The DOM consists of a tree-based API (compared to SAX, which is an event-based API). The Delphi unit xmldom.pas contains the definitions for IDOMDocument , IDOMNode , IDOMNodeList , IDOMAttr , IDOMElement , and IDOMText for this purpose. The C++Builder imported header file xmldom.hpp contains the typecasts of these interfaces into _di_IDOMDocument , _di_IDOMNode , _di_IDOMNodeList , _di_IDOMAttr , _di_IDOMElement , and _di_IDOMText , respectively.

Apart from the standard DOM interface, the TXMLDocument component also directly implements the Delphi interface IXMLDocument (typecast into _di_IXMLDocument ), 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 IXMLNode Collection are defined in the XMLIntf.pas unit (and typecast in XMLIntf.hpp to _di_IXMLDocument , _di_IXMLNode , _di_IXMLNodeList , and _di_IXMLNodeCollection ).

Although _di_IDOMDocument is also available through the DOMDocument property, the _di_IXMLDocument is an easier and the preferred way (in C++Builder 6) to work with the TXMLDocument component, so let's examine the _di_IXMLDocument interface in some more detail now.

Reading XML Documents

To put the XML document programming theory into practice, let's now build an example application using the TXMLDocument component. Start a new Application, save the main form in the default Unit1 , and the project in Project1 . Drop a TXMLDocument component next to a TMemo and TButton component. Set the Caption property of the TButton component to XML Doc and its Name to btnXMLDoc (see Figure 13.1).

Figure 13.1. TXMLDocument component at design time.

graphics/13fig01.gif

Connect the FileName property of the TXMLDocument component to the BizSnap.xml file, and set the doAutoSave flag of the Options property to true . Now, we can open the TXMLDocument by setting the Active property to true as well. After the TXMLDocument is active, we 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 _di_IXMLNode .

We can traverse through the nodes in this hierarchy and use the TMemo component to display the nodes and their attributes we encounter along the way. First of all, the root node can be obtained using the DocumentElement property. After we have the root, we can get the attributes as well as child nodes. The following code for the OnClick event handler of btnXMLDoc 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.

 void __fastcall TForm1::btnXMLDocClick(TObject *Sender)  {    Memo1->Lines->Clear();    _di_IXMLNode Chapter = XMLDocument1->DocumentElement;    Memo1->Lines->Add("Chapter: " + Chapter->Attributes["Title"]);    _di_IXMLNode Section = Chapter->ChildNodes->GetNode(0);    Memo1->Lines->Add("Section: " + Section->Attributes["Title"]);    Memo1->Lines->Add("Components: " +      Section->ChildNodes->Nodes[        Section->ChildNodes->IndexOf("Components")]->GetText());    Memo1->Lines->Add("Wizards: " +      Section->ChildNodes->Nodes[        (AnsiString)"Wizards"]->GetText());  } 

Note that the Nodes property can be indexed with a name or index. If you want to use a name, however, you need to explicitly cast it to an AnsiString first.

The output of clicking the XMLDoc button can be seen in Figure 13.2.

Figure 13.2. XMLDocument Component at runtime.

graphics/13fig02.gif

Note that we have to make sure to give the exact names of the attributes and child nodes. If we supply an incorrect name for an attribute, for example, we get an exception of class EVariantTypeCastError telling us 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. In other words, we should be careful not to make accidental typing mistakes.

For each node, we 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 finally, each node can return its Text as well as XML representation.

Writing XML Documents

Apart from browsing through an XML document, we 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 we make a change to it. Another option, the doNodeAutoCreate , will make sure that if we try to access a node that doesn't exist yet, it will dutifully create one for us. This flag is set to true by default. 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->Nodes[(AnsiString)"Wizards"] , but the ChildNode->Nodes[(AnsiString)"Wizard"] instead, a new (empty) child node with name Wizard will have been added to the XML Document without an error message or warning of any kind.

Apart from changing the XML Document by accident , we 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 . To illustrate this, drop a second TButton component, call it btnNewSection and write the following code in the OnClick() event handler:

 void __fastcall TForm1::btnNewSectionClick(TObject *Sender)  {    _di_IXMLNode Chapter = XMLDocument1->DocumentElement;    Chapter->AddChild("Section");    _di_IXMLNode NewSection = Chapter->ChildNodes->GetNode(      Chapter->ChildNodes->Count-1);    NewSection->SetAttribute("Title", (AnsiString)"New Section Title");    NewSection->AddChild("Components");    NewSection->AddChild("Wizards");    XMLDocument1->SaveToFile();    AnsiString XML;    XMLDocument1->SaveToXML(XML);    ShowMessage(XML);  } 

Note the last few lines in the btnNewSectionClick() event handler, which were added to illustrate the fact that we can always call the SaveToFile() method of the TXMLDocument component (when called without arguments, it will use the value of the FileName property), and we can also call the SaveToXML() method to save the current contents of the TXMLDocument component as XML string.

Although we've seen some helpful methods, it's always much easier to perform these operations if you have some more design-time support from the C++Builder IDE. Specifically with Code Insight based on the layout or semantics of the underlying XML document, which will be available when using XML Data Binding ”the topic of the next section.


   
Top


C++ Builder Developers Guide
C++Builder 5 Developers Guide
ISBN: 0672319721
EAN: 2147483647
Year: 2002
Pages: 253

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net