TestComplete has an example of parsing an XML file out to the TestComplete log.
It will be a useful javascript learning exercise to extend this to parsing the xml to a javascript object tree , with recursive finder functions.
Probably so many bad practices evident in here… It creates a tree structure of type XMLNode which is recursed just for fun in the finder functions.
XML parsing pass 1:
/************************************************* XML Utils - Custom XML parser - BUT, probably should've just used XPath to navigate MSXML object - This should be a constructor and methods setup.. *************************************************/ var XMLNode = function() { this.name = ""; this.hasValue = false; this.value = ""; this.hasChildren = false; this.children = []; this.hasAttributes = false; this.attributes = []; this.parentNode = null; } var XMLNodeAttribute = function(name, value) { this.name = name; this.value = value; } function XMLToMemory(targetFile, logFolderID) { // Function parses XML file into simple tree memory structure var doc, node, s; // Create a COM object // If you have MSXML 4: // ***Doc = Sys.OleObject("Msxml2.DOMDocument.4.0");*** // If you have MSXML 6: doc = Sys.OleObject("Msxml2.DOMDocument.6.0"); doc.async = false; // Load data from file // We use the file created earlier doc.load(targetFile); // Report an error, if, for instance, the markup or file structure is invalid if(doc.parseError.errorCode != 0) { s = "Reason:\t" + doc.parseError.reason + "\n" + "Line:\t" + aqConvert.VarToStr(doc.parseError.line) + "\n" + "Pos:\t" + aqConvert.VarToStr(doc.parseError.linePos) + "\n" + "Source:\t" + doc.parseError.srcText; +"\n" + "File:\t" + targetFile; // Post an error to the log and exit LogError(logFolderID, "Utils.XMLToMemory: can not parse the file", s, 300, true, true, true); LogError(logFolderID, "ALLSTOP"); Runner.Stop(); } // Iterate through xml file // Obtain the first node node = doc.documentElement; // Process the node return(XMLToMemoryProcessNode(node, null)); } function XMLToMemoryProcessNode(nodeToAdd, parentNode) { var i, attrs, attr, childNodes; var thisNode = new XMLNode(); thisNode.parentNode = parentNode; // name thisNode.name = nodeToAdd.nodeName; // If the node value is not null, output it if( aqObject.GetVarType(nodeToAdd.nodeValue) != 1) { thisNode.hasValue = true; thisNode.value = aqConvert.VarToStr(nodeToAdd.nodeValue); } // Attributes? if( nodeToAdd.nodeName.charAt(0) != "\#") { // Excluded helper nodes from processing // Obtain the attribute collection and // output the attributes to the log attrs = nodeToAdd.attributes; thisNode.hasAttributes = attrs.length; for(i = 0; i < attrs.length; i++) { attr = attrs.item(i); thisNode.attributes.push(new XMLNodeAttribute(attr.nodeName, attr.nodeValue)) } } // Obtain the collection of child nodes childNodes = nodeToAdd.childNodes; // Processes each node of the collection thisNode.hasChildren = childNodes.length; for(i = 0; i < childNodes.length; i++) { thisNode.children.push(XMLToMemoryProcessNode(childNodes.item(i), thisNode)); } return thisNode; } function XMLFindElementByName(startingXMLNode, nodeName) { // startingXMLNode is of type XMLNode // searches for 1st occurance of named element in supplied tree // - returns its handle if found // - returns null if not var i, returnedNode; if (startingXMLNode.name == nodeName) { // found it return startingXMLNode; } // Not found so recurse into children for (i = 0; i < startingXMLNode.hasChildren; i++) { returnedNode = XMLFindElementByName(startingXMLNode.children[i],nodeName); if(!IsNullOrUndefined(returnedNode)) { return returnedNode; } } return null; } function XMLGetNamedAttribute(targetXMLNode, attrName) { // targetXMLNode is of type XMLNode // searches for occurance of named attribute in target element only // - returns attribute value if found // - returns null if not var i; for (i = 0; i < targetXMLNode.hasAttributes; i++) { if(targetXMLNode.attributes[i].name == attrName) { return IsNullOrUndefinedThenReplaceWith(targetXMLNode.attributes[i].value); } } return null; } function XMLFindElementByNameGetNamedAttribute(startingXMLNode, nodeName, attrName) { // startingXMLNode is of type XMLNode // searches for 1st occurance of named element in supplied tree // - returns null if not found // if found: // - searches in found element attributes by attrib name: // - returns null if not found // - returns attr value if found var ele = XMLFindElementByName(startingXMLNode, nodeName) if (ele != null) { var value = XMLGetNamedAttribute(ele, attrName) if (value != null) { return value; } } // fall through return null; } function XMLFindElementByNameWithNamedAttributeAndValue(startingXMLNode, nodeName, attrName, attrValue) { // startingXMLNode is of type XMLNode // searches for 1st occurance of element having name, and expected attribute & value matched // in supplied tree // - returns its handle if found // - returns null if not var i, returnedNode, foundAttrValue; if (startingXMLNode.name == nodeName) { // found named node, check attributes var foundAttrValue = XMLGetNamedAttribute(startingXMLNode, attrName); if ( !IsNullOrUndefined(foundAttrValue) && IsNullOrUndefinedThenReplaceWith(foundAttrValue,"") == attrValue) { // found it return startingXMLNode; } } // Not found so recurse into children for (i = 0; i < startingXMLNode.hasChildren; i++) { returnedNode = XMLFindElementByNameWithNamedAttributeAndValue(startingXMLNode.children[i],nodeName, attrName, attrValue); if(!IsNullOrUndefined(returnedNode)) { // found 1st occurance return returnedNode; } } return null; }
XML parsing pass 2 trying to be more OO:
/************************************************* XML Utils - Lindsay Ross 20140130 - Custom XML parser - Note: Could've used xpath in the find methods (on the raw DOC) but this - is an excellent JScript recusion example so leaving it as is - USE: var myvar = new XMLTree; // substantiate a new XMLTree myvar.PopulateFromXMLFile(targetFile, logFolderID); myvar.FindElementByName("batchID"); *************************************************/ function XMLTree() { this.name = ""; this.hasValue = false; this.value = ""; this.hasChildren = false; this.children = []; this.hasAttributes = false; this.attributes = []; this.parentNode = null; } XMLTree.prototype.attribute = function(name, value) // constructor { this.name = name; this.value = value; }; XMLTree.prototype.FindElementByName = function(nodeName) { // startingXMLNode is of type XMLNode // searches for 1st occurance of named element in supplied tree // - returns its handle if found // - returns null if not var i, returnedNode; if (this.name == nodeName) { // found it return this; } // Not found so recurse into children for (i = 0; i < this.hasChildren; i++) { returnedNode = XMLFindElementByName(this.children[i],nodeName); if(!IsNullOrUndefined(returnedNode)) { return returnedNode; } } return null; } XMLTree.prototype.GetNamedAttribute = function(attrName) { // targetXMLNode is of type XMLNode // searches for occurance of named attribute in target element only // - returns attribute value if found // - returns null if not var i; for (i = 0; i < this.hasAttributes; i++) { if(this.attributes[i].name == attrName) { return IsNullOrUndefinedThenReplaceWith(this.attributes[i].value); } } return null; } XMLTree.prototype.FindElementByNameGetNamedAttribute = function(nodeName, attrName) { // startingXMLNode is of type XMLNode // searches for 1st occurance of named element in supplied tree // - returns null if not found // if found: // - searches in found element attributes by attrib name: // - returns null if not found // - returns attr value if found var ele = this.FindElementByName(nodeName) if (ele != null) { var value = XMLGetNamedAttribute(ele, attrName) if (value != null) { return value; } } // fall through return null; } XMLTree.prototype.FindElementByNameWithNamedAttributeAndValue = function(nodeName, attrName, attrValue) { // startingXMLNode is of type XMLNode // searches for 1st occurance of element having name, and expected attribute & value matched // in supplied tree // - returns its handle if found // - returns null if not var i, returnedNode, foundAttrValue; if (this.name == nodeName) { // found named node, check attributes var foundAttrValue = this.GetNamedAttribute(attrName); if ( !IsNullOrUndefined(foundAttrValue) && IsNullOrUndefinedThenReplaceWith(foundAttrValue,"") == attrValue) { // found it return startingXMLNode; } } // Not found so recurse into children for (i = 0; i < this.hasChildren; i++) { returnedNode = this.FindElementByNameWithNamedAttributeAndValue(this.children[i],nodeName, attrName, attrValue); if(!IsNullOrUndefined(returnedNode)) { // found 1st occurance return returnedNode; } } return null; } XMLTree.prototype.PopulateFromXMLFile = function(targetFile, logFolderID) { // Function parses XML file into simple tree memory structure var doc, node, s; // Create a COM object // If you have MSXML 4: // ***Doc = Sys.OleObject("Msxml2.DOMDocument.4.0");*** // If you have MSXML 6: doc = Sys.OleObject("Msxml2.DOMDocument.6.0"); doc.async = false; // Load data from file // We use the file created earlier doc.load(targetFile); // Report an error, if, for instance, the markup or file structure is invalid if(doc.parseError.errorCode != 0) { s = "Reason:\t" + doc.parseError.reason + "\n" + "Line:\t" + aqConvert.VarToStr(doc.parseError.line) + "\n" + "Pos:\t" + aqConvert.VarToStr(doc.parseError.linePos) + "\n" + "Source:\t" + doc.parseError.srcText; +"\n" + "File:\t" + targetFile; // Post an error to the log and exit LogError(logFolderID, "Utils.XMLToMemory: can not parse the file", s, 300, true, true, true); LogError(logFolderID, "ALLSTOP"); Runner.Stop(); } // Iterate through xml file // Obtain the first node node = doc.documentElement; // Process the node return this.ProcessNode(node, null); } XMLTree.prototype.ProcessNode = function(nodeToAdd, parentNode) { // not called directly (normally) // is called by PopulateFromXMLFile var i, attrs, attr, childNodes; var thisNode = new XMLNode(); thisNode.parentNode = parentNode; // name thisNode.name = nodeToAdd.nodeName; // If the node value is not null, output it if( aqObject.GetVarType(nodeToAdd.nodeValue) != 1) { thisNode.hasValue = true; thisNode.value = aqConvert.VarToStr(nodeToAdd.nodeValue); } // Attributes? if( nodeToAdd.nodeName.charAt(0) != "\#") { // Excluded helper nodes from processing // Obtain the attribute collection and // output the attributes to the log attrs = nodeToAdd.attributes; thisNode.hasAttributes = attrs.length; for(i = 0; i < attrs.length; i++) { attr = attrs.item(i); thisNode.attributes.push(new this.attribute(attr.nodeName, attr.nodeValue)) } } // Obtain the collection of child nodes childNodes = nodeToAdd.childNodes; // Processes each node of the collection thisNode.hasChildren = childNodes.length; for(i = 0; i < childNodes.length; i++) { thisNode.children.push(XMLToMemoryProcessNode(childNodes.item(i), thisNode)); } return thisNode; } function xmlgo() { var sLogFile = "c:\\LRossForTestComplete\\winscplog.xml"; // Check Log file created if(!DoesFileExist(sLogFile)) { LogError(logFolderID, "WinSCP.RunFTPCommand: can not find Log File", sLogFile, 300, true, true, true); LogError(logFolderID, "ALLSTOP"); Runner.Stop; } // Parse LogFile into memory var logFolderID = -1; var xmltree = new XMLTree(); var xmltree2 = xmltree.PopulateFromXMLFile(sLogFile, logFolderID); var xmltree3 = new XMLTree.PopulateFromXMLFile(sLogFile, logFolderID); // Search for Result element var resultNode = XMLFindElementByName(xmltree2, "result"); if (IsNullOrUndefined(resultNode)) { LogError(logFolderID, "WinSCP.RunFTPCommand: XML Log: 'result' element not found", "", 300, true, true, true); LogError(logFolderID, "ALLSTOP"); Runner.Stop; } var result = XMLGetNamedAttribute(resultNode, 'success') if (IsNullOrUndefined(result)) { LogError(logFolderID, "WinSCP.RunFTPCommand: XML Log: 'result' element found, but 'success' attribute not found", "", 300, true, true, true); LogError(logFolderID, "ALLSTOP"); Runner.Stop; } if (result != "true") { LogError(logFolderID, "WinSCP.RunFTPCommand: XML Log: 'result' element, 'success' element is not 'true', got: '" + result, "", 300, true, true, true); LogError(logFolderID, "ALLSTOP"); Runner.Stop; } }
xxxx