The cXMLDOMDocument class provides the Interface to the root node of an XML document. It includes methods and properties used to obtain or create all other XML objects. An object of the cXmlDomDocument class is a grouping of nodes that logically describe an XML document. There is a root element, from which all other elements descend in a tree-like structure.
The cXMLDOMDocument class is your starting point for XML processing. Normally you create a cXMLDOMDocument and then create all of your other XML node objects dynamically, destroying those nodes when they are no longer needed. This section contains a good deal of introductory material designed to guide you through all of the XML classes and messages. This section is divided into the following sections:
XML Basics - an XML Overview
Examples - XML Sample code
None of the above examples demonstrate how to handle XML documents that use namespaces. For information about XML documents with namespaces refer to: Processing XML Documents with Namespaces.
Extensible Markup Language (XML) is a markup language for describing structured data in a text file or elsewhere. It is not the purpose of this Help to describe the theory of XML, but rather the DataFlex support for it. There are many good books written on XML which you can find in local bookstores and the local library. In addition, you can find much information on the Internet.
DataFlex supports XML through an interface consisting of several classes, which allow you to access elements in an XML document; work on them, then save them back into the document. Each element you request is returned as a fully-formed DataFlex Object for you to work on. When you are done, it is saved back into the document.
The top-level DataFlex interface is simple and powerful but, for advanced use, you can also access the lower levels if you wish. For this reason, all the classes and messages are given here, but you can often achieve the results you want by using only the top-level interface.
The following is a very brief reminder of the essentials you need to keep in mind when using the XML classes.
While Hypertext Markup Language (HTML) specifies how to display data in a browser (e.g. bold or italic), XML defines the content of the data as elements (e.g. Name and Address). In XML, you then use stylesheets to describe the presentation. You can then use different stylesheets and applications to present and process the same data differently in different browsers.
The structure of an XML document can be implied through its contents, or it can be formally defined, either by a Document Type Definition (DTD) or by a Schema. A DTD describes the rules of the document it accompanies, whereas a Schema is an extensible language which users can augment with additional information such as data types, inheritance, and presentation rules.
Data sent with a DTD or Schema is known as "valid XML". As long as it follows a few rules, data sent without a DTD or Schema is known as "well-formed" and implicitly describes itself. The rules are: Each start tag must have a corresponding end tag (and tags are case-sensitive); Elements must not overlap; Certain characters (<, >) cannot be used without special treatment, and each XML document must have a unique root element.
In HTML, there is always a single Document object, which always has one or more Forms child objects, each of which has a single Images Collection child object. In XML, there is no advance knowledge of the structure of a document, and objects can be named anything you like. For this reason, XML documents are thought of as trees, with multiple nodes. Some will have no children of their own, and some will have collections of other nodes as children. A Node can only have one parent, and either zero or one collection of children. Some nodes can also have attributes, and each such attribute is itself a node..
There are various types of nodes, such as Text, Element, Comment, Entity Reference, Processing Instructions.
Some types of node (mostly the document and element nodes) may have an attribute property which contains either a reference to a NamedNodeMap containing the attribute node objects, or null if there are none.
Every node also has a childnodes property which contains either a reference to a NodeList containing a list of the child node objects, or null if there are none.
The key to understanding XML support in DataFlex is the Document Object Model (DOM). You can think of this as a tree-view with each branch being a node. In DOM you parse a document on a node-by-node basis. You never create all the nodes needed to represent a document. Instead you get a handle to the node or nodes you need, you work on them, then destroy the handle when no longer needed. Destroying the handle does not affect the actual XML document - just your access to it via a node handle.
In DOM the handles are referred to as interface handles. You ask an XML document to create an interface handle for a particular part of the XML structure. In DataFlex, we wrap the interface handles inside a DataFlex object. Actually, we create a DataFlex node object and bind the interface handle to the object. We talk to the node via DataFlex messages. When we are done, we destroy the DataFlex object which destroys the interface handle (one must always dispose of the interfaces).
The DataFlex model uses a very dynamic object creation/destruction scheme. You never really name the objects - you refer to them by their object handle (DataFlex Object ID). You call a function that says: "create me an XML node object, bind it to the following interface handle, and return its DataFlex object handle". You then work on the DataFlex node object. When done, you send a message saying "destroy the DataFlex node object".
Any message that returns or sends a handle is using DataFlex object handles. With the exception of the cXmlDomDocument object, all other objects are created by sending a message to the Document object or some other node object. When needed, a DataFlex object will be created and the object's ID (handle) will be returned. The object will be based on the appropriate class type, determined by the message being sent and the data being retrieved.
When you no longer need the node object it should be destroyed. This is done by sending the message Destroy to the object. This is extremely important. Any time an object handle is returned from an XML message, you have created a new object. It is your responsibility to destroy it when you no longer need it. If you forget to destroy an object it will be properly destroyed when you destroy its parent. Since all objects reside within an Xml document object, destroying the document object will properly dispose of child objects. However, this is considered bad technique.
You can traverse the Document tree by using property handles (and similarly-named functions) such as get DocumentObject, ParentNode, FirstChild, LastChild, NextSibling and PreviousSibling.
You can change the structure of the loaded XML document with methods such as CreateChildNode, CloneNode, RemoveNode, AddElement (procedure), AddElement (function), AddAttribute and others. You can change the content of nodes by setting read/write properties such as psNodeValue.
There is no message that allows you to create a new DTD (it's not part of the Microsoft DOM specification.) The only way to create a new XML document with a DTD declaration is to create a new XML document and load a skeleton XML document that contains a DTD. You can do this by using LoadXML.(or LoadXMLDocument.)
Functions that fail when creating nodes return a 0. This is used by developers as normal error checking. All the node creation functions return an object handle. If the handle is zero, then no object was created and, most likely, an error that should be dealt with occurred.
Procedures that fail when creating nodes generate an error. Any of the Send AddXxxxxx messages (Send AddElement, Send AddChildComment, etc.) will generate an error DFERR_XML_INTERNAL_ERROR (4380) if the object can not be created and added. This is the only way you could tell if the operation succeeded or not. The assumption is made that you will never use the messages unless you are sure there will not be an error. If an error if possible you should use functions (Get CreateXxxxx followed by Get AppendNode or Get InsertBeforeNode) where you can check the return value to make sure the operation suceeded and proceed as needed.
The cXmlDomDocument class is the starting point for most XML processing. Refer to the cXmlDomDocument class for more information about the XML Document Object Model (DOM) and using XML within DataFlex. Samples are provided in this section.
If you are parsing or writing XML documents that use namespaces (i.e. where the xmlns attribute is used to declare one or more namespace names), then you must use special namespace-aware methods to read, write or traverse the document's elements and attributes.
The following lists the namespace aware methods you should use:
AddAttributeNS, AddElementNS, AttributeValueNS, AttributeValueNodeNS, ChildElementNS, ChildElementValueNS, IsElementNS, NextElementNS, RemoveAttributeNS, SetChildElementValueNS
For more information refer to the relevant help topic for these methods.
The XML classes are derived from one of two super-classes - a cXmlDom Node class, or a cXmlDom Collection class.
The Node class exists in DataFlex as the cXmlDomNode class and is the super-class for all of the other XML node classes (document, elements, attributes, etc.) When you need to create access to a node you use one of these classes.
cXmlDomNode contains the interface to handle all processing needs common to all node classes. Its sub-classes contain specialized messages that apply to each special node type. For this reason, it is wise to thoroughly understand the cXmlDomNode interface - it is used by all of the other classes.
While you will normally create an object based on the special XML node type you are working with, you can use the cXmlDomNode class to represent any XML node, but then you will be limited to using the generic node interface.
The most commonly used node objects are:
cXmlDomDocument - this represents your entire XML document. Typically you will create this object using standard DataFlex object-creation syntax. All other XML objects will be created, dynamically, within this object. A normal lifespan of a document object is:
cXMLDomElement - this is is used to represent XML elements.
cXMLDomAttribute - this is used to represent attributes of an element.
Collection classes allow you to create an object that contains a collection of others nodes or attributes. FleXML has two types of collection objects - cXmlDomNodeList and cXmlDomNamedNodeMap. The cXmlDomNodeList is used to contain collections of other XML nodes (e.g. a collection of elements that all match a specified search pattern). The cXmlDomNamedNodeMap is used to contain collections of attributes (attributes are not considered to be nodes - although you can create attribute nodes).
The DataFlex methods (messages) allow you to traverse the DOM tree and manipulate its nodes. Each node is defined as a specific node type, which also defines valid parent and child nodes for each node type. For most XML documents, the most common node types are element, attribute and text.
The following examples show different aspects of using XML within DataFlex
None of the above examples demonstrate how to handle XML documents that use namespaces. For information about XML documents with namespaces refer to: Processing XML Documents with Namespaces.
This sample shows you how to access an XML Document. To access an XML document for editing or reporting purposes you do the following:
The translates into DataFlex code as follows:
Object oBPOXML is a BusinessProcess : Procedure OnProcess Handle hoXML hoRoot hoList hoCust Integer iItems i bOK Get Create (RefClass(cXMLDOMDocument)) to hoXML // you can use Create (as above) or create // the object using standard Object syntax (as below) // Object oXML is a cXmlDomDocument // Move Self to hoXML // End_Object Set psDocumentName of hoXML to "http://localhost/xml/customer.xml" Set pbAsync of hoXML to False Set pbValidateOnParse of hoXML to True Get LoadXMLDocument of hoXML to bOK If not bOK Begin Send BasicParseErrorReport of hoXml Procedure_Return End Showln 'loaded' Get DocumentElement of hoXML to hoRoot Get FindNodeList of hoRoot "CUSTOMER" to hoList Get NodeListLength of hoList to iItems Decrement iItems For i from 0 to iItems Get CollectionNode of hoList i to hoCust Send ShowCust hoCust Send Destroy of hoCust Loop Send Destroy of hoList Send Destroy of hoRoot Send Destroy of hoXML End_Procedure // OnProcess Procedure ShowCust Handle hoCust String sName sPhone sState Get AttributeValue of hoCust "NAME" to sName Get ChildNodeValue of hoCust "TELEPHONE" to sPhone Get ChildNodeValue of hoCust "ADDRESS/STATE" to sState Showln sName ' ' sPhone ' ' sState End_Procedure End_Object // oBPOXML
The following program displays the attributes for an element on the screen.
Use dfallent.pkg use Flexml.pkg Object oTest Is A Panel Procedure Test Integer i // Index used to iterate through the list of attributes Integer bOk // Boolean to test return value Handle hoXML // Handle to XML document object Handle hoRootNode // Handle to root node of document Handle hoNode // Handle to XML interface of search node Handle hoAttributeCollection // Handle to collection of attributes Integer iAttributeCount // Number of attributes of node found Handle hoAttributeNode // Hndl to dynamic object to access atributes String sQueryString // String to query for search node String sAttributeName // String that holds each attribute's name String sAttributeValue // String that holds each attribute's value // Create document object Get Create (RefClass(cXmlDomDocument)) to hoXML // Set the name of the document Set psDocumentName of hoXML to ".\Customer.xml" // Turn on the xml parse test so errors are reported. Set pbValidateOnParse of hoXML to True // Load the document Get LoadXMLDocument of hoXml to bOK // If the document fails to load, abort the program If Not bOK begin Send BasicParseErrorReport of hoXML Send Destroy of hoXML Procedure_Return End // Search from the root Get DocumentElement of hoXML to hoRootNode if (hoRootNode = 0) Begin Error 10001 "Cannot get the root node for the XML document test.xml" Send Destroy of hoXml Procedure_Return End // Do a query for a customer whose name is "Ming Foo Bakery" Move ("CUSTOMER[@NAME='Ming Foo Bakery']") to sQueryString Get FindNode of hoRootNode sQueryString to hoNode If (hoNode=0) Begin Error 10002 "Cannot find customer Ming. in the XML document test.xml" End Else Begin // Now do test - get attribute collection Get AttributeNodes of hoNode to hoAttributeCollection If (hoAttributeCollection = 0) Begin ERROR 10003 "Cannot get collection of attributes for Ming. node" End Else Begin // Show the attributes of the collection. // First, get the number of items in the set Get NodeListLength of hoAttributeCollection to iAttributeCount // Now, iterate through the list of items. For i From 0 to (iAttributeCount-1) // Get each attribute (indexed by i) - // a dynamic object is returned. Get CollectionNode of hoAttributeCollection i to hoAttributeNode // Get the name of the attribute. Get psNodeName of hoAttributeNode to sAttributeName // Get its value. Get psText of hoAttributeNode to sAttributeValue // Show on the screen. Showln "Attribute " sAttributeName " has the value " sAttributeValue // Destroy the attribute object. Send Destroy of hoAttributeNode Loop Send Destroy of hoAttributeCollection End Send Destroy of hoNode End // Cleanup. Destroy objects that were created. Send Destroy of hoRootNode Send Destroy of hoXML End_Procedure End_Object Send Test of oTest Send Stop_Box "Program has completed"
The following program accesses data from a datafile and writes it to an XML file. For full explanations and commented code, see the XML-Sample.src sample project in the 'Specialized Components' sample workspace.
Use Flexml.pkg Use Customer.dd ACTIVATE_VIEW Activate_oXMLSample2 FOR oXMLSample2 Object oXMLSample2 is a dbView Set Label to "XML - Sample 2" Set Size to 262 412 Set Location to 7 5 Object oSaveCustomertoXML is a BusinessProcess Property Integer pbAllowDuplicates False Set Location to 2 388 Set Status_Panel_State to False Object Customer_DD is a Customer_DataDictionary End_object Procedure OnProcess Handle hoXML hoRoot hoList hoCust hoDD Integer iItems i bErr bAllowDuplicates bOk String sDataPath Get pbAllowDuplicates to bAllowDuplicates Get Create (RefClass(cXMLDOMDocument)) to hoXML Get psDataPath of (phoWorkSpace(oApplication)) to sDataPath Set psDocumentName of hoXML to (sDatapath - "\Mycustomer.xml") Set pbValidateOnParse of hoXML To True Get LoadXMLDocument of hoXML to bOK Get DocumentElement of hoXML to hoRoot If not hoRoot ; Get CreateDocumentElement of hoXML "Customers" to hoRoot Move Customer_DD to hoDD Send Clear of hoDD Send Find of hoDD GT 1 While (found) Send AddCustomer hoRoot bAllowDuplicates Send Find of hoDD GT 1 end Send Destroy of hoRoot Get SaveXMLDocument of hoXML to bErr Send Destroy of hoXML End_Procedure Procedure AddCustomer handle hoRoot integer bAllowDuplicates handle hoCust hoAdd hoEle string sQuery If not bAllowDuplicates begin Move ("Customer[@Customer-Id='"+trim(Customer.Customer_Number)+"']") to sQuery Get FindNode of hoRoot sQuery to hoCust If hoCust Begin Send Destroy of hoCust Procedure_return end end Get AddElement of hoRoot "Customer" "" to hoCust Send AddAttribute of hoCust "Customer-Id" (trim(Customer.Customer_Number)) Send AddElement of hoCust "Name" (trim(Customer.Name)) Get AddElement of hoCust "Address" "" to hoAdd Send AddElement of hoAdd "Street" (trim(Customer.Address)) Send AddElement of hoAdd "City" (trim(Customer.City)) Send AddElement of hoAdd "State" (trim(Customer.State)) Send AddElement of hoAdd "Zip" (trim(Customer.Zip)) Send Destroy of hoAdd Send AddElement of hoCust "Telephone" (trim(Customer.Phone_Number)) Send Destroy of hoCust End_Procedure End_Object Object oWriteCustomertoXML is a Button Set Label to "Write Customers to XML file" Set Size to 14 91 Set Location to 210 208 Procedure OnClick Send DoProcess of oSaveCustomertoXML Send Info_Box "XML File has been Created" End_Procedure End_Object End_Object
The following program accesses data from multiple related datafiles and writes it to an XML file. For full explanations and commented code, see the examples in the "..\DFx\Examples\XML-Sample\AppSrc" directory.
Use Flexml.pkg Open customer Open SalesP Open vendor Open Invt Open OrderHea Open OrderDtl ACTIVATE_VIEW Activate_oXMLSample4 FOR oXMLSample4 Object oXMLSample4 is a dbView Set Label to "XML4" Set Size to 286 435 Set Location to 1 2 Object oBPOXMLWrite1 is a BusinessProcess Set Location to 196 96 Set Display_Error_State to TRUE Set Status_Panel_State to FALSE Property Integer pbUseCData False Function Encode string sDat Returns string Move (Replaces("&",sDat,"&")) to sDat Move (Replaces("<",sDat,"<")) to sDat Move (Replaces(">",sDat,">")) to sDat Move (Replaces('"',sDat,""")) to sDat Function_Return sDat End_Function Procedure OnProcess Integer bTest Handle hoRoot hoXML string sDataPath Get psDataPath of (phoWorkSpace(oApplication)) to sDataPath Get Create (RefClass(cXMLDOMDocument)) to hoXML Set psDocumentName of hoXML to (sDatapath - "\records-1.XML") Get LoadXMLDocument of hoXML to bTest Get DocumentElement of hoXML to hoRoot If Not hoRoot ; Get CreateDocumentElement of hoXML "Tables" to hoRoot Showln "Writing XML" Send OutPutTable hoRoot Customer.File_number Send OutPutTable hoRoot SalesP.File_number Send OutPutTable hoRoot OrderHea.File_number Send OutPutTable hoRoot OrderDtl.File_number Send OutPutTable hoRoot Invt.File_number Send OutPutTable hoRoot Vendor.File_number Send Destroy Of hoRoot Get SaveXMLDocument of hoXML to bTest Showln "Saving XML" Send Destroy Of hoXML Showln "Done" End_Procedure Procedure OutputTable handle hoRoot integer iFile String sFile sQuery Handle hoTable Integer bRetVal bUseCData Get pbUSeCData to bUseCData Get_Attribute DF_FILE_LOGICAL_NAME of iFile to sFile Move ("Table[@Name='" + sFile + "']") to sQuery Get FindNode of hoRoot sQuery to hoTable If hoTable Begin Get YesNo_Box (sFile * "exists. Replace?") to bRetVal If (bRetVal=MBR_No) Begin Send destroy of hoTable Procedure_return end Else Begin Get RemoveNode of hoRoot hoTable to hoTable Send destroy of hoTable End End Showln "Writing " sfile Get AddElement of hoRoot "Table" "" to hoTable Send AddAttribute Of hoTable "Name" sFile Send AddAttribute Of hoTable "File" iFile Clear iFile VFind iFile 0 GT While (Found) Send OutputRows hoTable iFile bUseCData VFind iFile 0 GT End Send Destroy of hoTable End_procedure Procedure OutPutRows ; handle hoNode integer iFile integer bUseCData integer iField iFields bRetVal iType string sTag sField handle hoRow hoField Get AddElement of hoNode "Row" "" to hoRow Get_Attribute DF_FILE_NUMBER_FIELDS of iFile to iFields For iField from 1 to iFields Get_Attribute DF_FIELD_TYPE of iFile iField to iType If (iType<>DF_OVERLAP) Begin Get_Field_Value iFile iField to sField Move (trim(sField)) to sfield Get_Attribute DF_FIELD_NAME of iFile iField to sTag Get AddElement of hoRow "Field" "" to hoField If bUseCData ; Send AddCDataSection of hoField sField Else Begin Get Encode sField to sField // encode < > ; and " Send AddChildTextNode of hoField sField End Send AddAttribute Of hoField "Field-number" iField Send AddAttribute Of hoField "Name" sTag Send Destroy of hoField end Loop Send Destroy of hoRow End_procedure End_Object Object oWriteXml1 is a Button Set Label to "Write Tables to XML " Set Size to 14 82 Set Location to 218 6 Procedure OnClick send DoProcess of oBPOXMLWrite1 End_Procedure End_Object End_Object
The following program shows how to navigate a list of nodes from first to last.
Use DFAllent.pkg Use Flexml.pkg Object oTest Is A cObject Procedure ShowInformation Integer hoNode Integer iLineCount String sName Integer iType // If the node is an element (e.g. Customer), get the element's nodename and the attribute value for NAME // skip all non-element nodes Get piNodeType of hoNode to iType If (iType = NODE_ELEMENT) Begin Get AttributeValue of hoNode "NAME" to sName Showln iLineCount ": Node is [" (psNodeName(hoNode)) "] Name is [" sName "]" End End_Procedure Procedure Test Integer bOk // Boolean to test return value. Integer hoXML // Handle to XML Document object Integer hoRoot // Handle to root node of document. Integer hoNode // handle to a node for testing Integer hoNextNode // used to find the previous node Integer iLineCount // Create document object Get Create (RefClass(cXmlDomDocument)) to hoXML Set psDocumentName of hoXML to ".\Customer.xml" // Turn on the xml parse test so errors are reported. Set pbValidateOnParse of hoXML to True // Load the document Get LoadXMLDocument of hoXml to bOK // If the document fails to load, abort the program If nOT bOK begin Send BasicParseErrorReport of hoXml Send Destroy of hoXML Procedure_Return End // Search from the root. Get DocumentElement of hoXml to hoRoot if (hoRoot=0) Begin Error 10001 "Cannot get the root node for the XML document test.xml" Send Destroy of hoXml Procedure_Return End // traverse through nodes. Start with first child and then // move through each sibling Move 0 to iLineCount Get FirstChild of hoRoot to hoNode // get the first node While (hoNode<>0) Increment iLineCount Send ShowInformation hoNode iLineCount Get NextNode of hoNode to hoNode // now get next sibling node, destroy current node Loop Send Destroy of hoRoot Send Destroy of hoXml End_Procedure End_Object Send Test of oTest Send Stop_Box "Program has completed"
The following program demonstrates the use of phLastChild to traverse a list of nodes backwards.
Use DFAllent.pkg Use Flexml.pkg Object oTest Is A cObject Procedure ShowInformation Integer hoNode Integer iLineCount String sName Integer iType // If the node is an element (e.g. Customer), get the element's nodename and the attribute value for NAME // skip all non-element nodes Get piNodeType of hoNode to iType If (iType = NODE_ELEMENT) Begin Get AttributeValue of hoNode "NAME" to sName Showln iLineCount ": Node is [" (psNodeName(hoNode)) "] Name is [" sName "]" End End_Procedure Procedure Test Integer bOk // Boolean to test return value. Integer hoXML // Handle to XML Document object Integer hoRoot // Handle to root node of document. Integer hoNode // handle to a node for testing Integer hoNextNode // used to find the previous node Integer iLineCount // Create document object Get Create (RefClass(cXmlDomDocument)) to hoXML Set psDocumentName of hoXML to ".\Customer.xml" // Turn on the xml parse test so errors are reported. Set pbValidateOnParse of hoXML to True // Load the document Get LoadXMLDocument of hoXml to bOK // If the document fails to load, abort the program If not bOK begin Send BasicParseErrorReport of hoXml Send Destroy of hoXML Procedure_Return End // Search from the root. Get DocumentElement of hoXml to hoRoot if (hoRoot=0) Begin Error 10001 "Cannot get the root node for the XML document test.xml" Send Destroy of hoXml Procedure_Return End // traverse through nodes in backwards order. Start with last child and then // move up through each sibling Move 0 to iLineCount Get LastChild of hoRoot to hoNode // get the last node While (hoNode<>0) Increment iLineCount Send ShowInformation hoNode iLineCount Get PreviousSibling of hoNode to hoNextNode // now get previous sibling node Send Destroy of hoNode // Important: get rid of the previous node object Move hoNextNode to hoNode Loop Send Destroy of hoRoot Send Destroy of hoXml End_Procedure End_Object Send Test of oTest Send Stop_Box "Program has completed"
This sample shows how to insert an XML declaration that specifies an XML version of 1.0 and encoding of ISO8859-1 into an XML document. The inserted XML code will look like this:
<?xml version="1.0" encoding="ISO8859-1"?>
Get CreateChildProcessingInstruction Of hoXMLDocumentRoot "xml" 'version="1.0" encoding="UTF-8" standalone="yes"' To hoNodeToInsert If (hoNodeToInsert > 0) Begin Get InsertBeforeNode Of hoXML hoNodeToInsert hoXMLDocumentRoot To hoInsertedNode If ((hoInsertedNode <> hoNodeToInsert) and (hoInsertedNode > 0)) Begin Send Destroy Of hoInsertedNode End Send Destroy Of hoNodeToInsert End
See AddElementNS for an example of creating a new XML document.
XML Namespaces Primer | cXmlDomAttribute | cXmlDomCDataSection | cXmlDomComment | cXmlDomDocumentFragment | cXmlDomDocumentType | cXmlDomElement | cXmlDomEntity | cXmlDomEntityReference | cXmlDomNamedNodeMap | cXmlDomNode | cXmlDomNodeList | cXmlDomNotation | cXmlDomProcessingInstruction | cXmlDomTextNode | BaseXmlDomParseError