I am posting the following function in hope that someone else can use it without pulling their hair out trying to figure out XML parsing. The following function just traverses a simple INI setting type XML file (you can easily tailor this to your needs). This is probably one of the more basic things you can do with the MS XML parser but my hope is that it produces a good starting point. The XMLAdapter class is not used because it was very weak for just reading values (I didn't want to use cursors at all).
References for the MSXML2.DomDocument object for use in FoxPro can be found in the articles section "GETTING TO GRIPS WITH THE MICROSOFT DOM OBJECT" by Simon Arnold and an excellent reference for the DOM object in general which lists details about every attribute and every function can be found here:
http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.htmlSample XML File ReadXML() will parse (make sure it is saved as UTF-8 format or specify "us-ascii" for a plain text file in the encoding):
<?xml version="1.0" encoding="UTF-8"?>
<system>
<customer>
<name my_attribute="XMLRules">XYZ Plus</name>
<address1>777 W. N. Blvd.</address1>
</customer>
<settings>
<usepw>yes</usepw>
<reporting>no</reporting>
</settings>
</system>
*******************************************************
* ReadXML
*
* Read an XML file of a structured format that would
* be similiar to an INI settings file, traverses
* through the entire structure as an example
*******************************************************
FUNCTION ReadXML
LPARAMETERS pcXMLFile
LOCAL oXML, oRootNode, cParentName, cValue, cName, nType, nNumNodes, ;
cRootTagName, oNodeList, oNode, bHasChild, cTagName, ;
oChildNodeList, nChildLen, nPass, oChildNode, oAttributeList, ;
cTextData, nTextDataLen, cAttrName, cAttrValue, nNumAttr
* Start out by creating the actual xml parser object
oXML = CREATEOBJECT('MSXML2.DomDocument')
* Wait for the document to be parsed and loaded
oXML.ASYNC = .F.
* Load the document into the object, if this was a stream instead
* of a file name, we would use loadXML(cCharStream)
oXML.LOAD(pcXMLFile)
* Get the root element
oRootNode = oXML.documentElement
* What is the root tag name?
cRootTagName = oRootNode.tagName
* Get all the nodes in the document with the special '*'
* parameter, we could just pass in a tag name to get the
* node list for that specific tag
oNodeList = oRootNode.getElementsByTagName("*")
* How many nodes did we retrieve
nNumNodes = oNodeList.LENGTH
* Go through all the nodes in the NodeList.
* Note that Attribute and Character/Text Data is NOT
* counted as part of this list, you must get that data
* separately, this list only contains tag elements
* Note that this uses C like array positioning by
* starting at zero
FOR nPos = 0 TO (nNumNodes-1) STEP 1
* Get the next node in the list
oNode = oNodeList.ITEM(nPos)
* What is the value of this node, if it is an element
* then this value is the tag name
cParentName = oNode.nodeName
* Does this node have any children?
bHasChild = oNode.hasChildNodes()
* What is the node type, element or text?
nType = oNode.nodeType
IF nType = 1
* This is an element/tag so it may have
* attributes. We can get those attributes
* by name or in a list.
* Since this example function traverses thru
* the xml tree, it would not be very efficient
* to query every single node for a particular
* attribute, this is just to show how it could
* be done.
* if the attribute does not exist, returns .NULL.
* otherwise we get the attribute value
cAttrValue = oNode.getAttribute("my_attribute")
* We could also get a NamedNodeMap accessing
* the attributes property
oAttributeList = oNode.attributes
* how many attributes do we have
nNumAttr = oAttributeList.length
* Get the attribute using the list
cAttrValue = oAttributeList.getNamedItem("my_attribute")
ENDIF
IF bHasChild
* Ok, we know we have children but what type are they?
* Just test the first one to see if it is something
* other than an element and if so, get it
IF oNode.firstChild.nodeType != 1
* We know we have something other than an element, get
* the tag name of the element we are parsing
cTagName = oNode.tagName
* Get the node list and determine how man child
* nodes this element has
oChildNodeList = oNode.childNodes
nChildLen = oChildNodeList.LENGTH
* Go through all child nodes and grab the non-element
* data to do with what you like
FOR nPass = 0 TO (nChildLen-1) STEP 1
oChildNode = oChildNodeList.ITEM(nPass)
cValue = oChildNode.nodeValue
cName = oChildNode.nodeName
nType = oChildNode.nodeType
bHasChild = oChildNode.hasChildNodes()
* For now just look for text types, other
* types can be added later if needed
DO CASE
CASE nType = 3
* Text node
cTextData = oChildNode.DATA
nTextDataLen = oChildNode.LENGTH
CASE nType = 4
* CData Section node
cTextData = oChildNode.DATA
nTextDataLen = oChildNode.LENGTH
OTHERWISE
* Some other node we don't care about
* right now
ENDCASE
ENDFOR
ENDIF
ENDIF
ENDFOR
RETURN 0
ENDFUNC