Parsing and Consuming the CRM 2011 SOAP Service Inside JavaScript

Last time we looked at how to execute a FetchXML query against the CRM service within JavaScript.  Now that we have the response, I wanted to write something to parse the XML and store it in an easily consumable variable.

Now there are plenty of ways to parse the XML and this approach may not be a fan of Big O notation Winking smile, but it’s quite fast.

Establishing Our Needs

First let’s layout how I intend to consume the response.  Here’s an example usage, that I’d like to be able to have:

sOut += "<table><tr><th>Account ID</th><th>Account Name</th><th>Customer Type</th><th>Transaction Currency</th></tr>";

for (var i = 0; i < results.length; i++){
    sOut += "<tr>" +
            "<td>" + results[i].attributes["accountid"].value + "</td>" +
            "<td>" + results[i].attributes["name"].value + "</td>" +
            "<td>" + results[i].attributes["customertypecode"].formattedValue + "</td>" +
            "<td>" + results[i].attributes["transactioncurrencyid"].name + "</td>" +
            "</tr>";
}
sOut += "</table>";

  • My first criteria is for the results to be an array of entities.
  • Secondly, I want to be able to consume a specific attribute by simply specifying the attribute name. 
    Note: If you need to iterate through the attributes, you can do so by parsing the FetchXML query.
  • I will also store the GUID and Logical Name associated with this entity.
  • Finally, when linking entities I will be able to consume them similar to C# with the entityN.attribute (e.g. contact1.firstname).

 

Watching from Developer Tools

image

By looking at the JavaScript variable from within IE Developer Tools, we can see a very nice and clean array of entities.  I’ve included the type of the attribute, so that we can use the Formatted Value of the OptionSetValue.  I’ve also setup an Entity Reference object with the Logical Name, GUID, and Name.  This will ensure that you can display the proper description if desired.

Code changes

First, we need to swap in the new Fetch Callback

FetchUtil.prototype._FetchCallback = function(xmlhttp,callback)
{
    ///<summary>(private) Fetch message callback.</summary>
    //xmlhttp must be completed
    if (xmlhttp.readyState != XMLHTTPREADY)
    {   
        return;
    }
   
    //check for server errors
    if (this._HandleErrors(xmlhttp))
    {
        return;
    }
    
    var sFetchResult = xmlhttp.responseXML.selectSingleNode("//a:Entities").xml;
   
    var resultDoc = new ActiveXObject("Microsoft.XMLDOM");
    resultDoc.async = false;
    resultDoc.loadXML(sFetchResult);
   
    //parse result xml into array of jsDynamicEntity objects
    var results = new Array(resultDoc.firstChild.childNodes.length);
    for( var i = 0; i < resultDoc.firstChild.childNodes.length; i++ )
    {
        var oResultNode = resultDoc.firstChild.childNodes[i];
        var jDE = new jsDynamicEntity();
        var obj = new Object();
       
        for( var j = 0; j < oResultNode.childNodes.length; j++ )
        {
            switch (oResultNode.childNodes[j].baseName){
                case "Attributes":
                    var attr = oResultNode.childNodes[j];
                    
                    for ( var k = 0; k < attr.childNodes.length; k++){
                   
                        // Establish the Key for the Attribute
                        var sKey = attr.childNodes[k].firstChild.text;
                        var sType = '';
                   
                        // Determine the Type of Attribute value we should expect
                        for(var l = 0; l < attr.childNodes[k].childNodes[1].attributes.length; l++){
                            if (attr.childNodes[k].childNodes[1].attributes[l].baseName == 'type'){
                                sType = attr.childNodes[k].childNodes[1].attributes[l].text;
                            }
                        }
                       
                        switch (sType){
                            case "a:OptionSetValue":
                                var entOSV = new jsOptionSetValue();
                                entOSV.type = sType;
                                entOSV.value = attr.childNodes[k].childNodes[1].text;
                                obj[sKey] = entOSV;
                                break;
                               
                            case "a:EntityReference":
                                var entRef = new jsEntityReference();
                                entRef.type = sType;
                                entRef.guid = attr.childNodes[k].childNodes[1].childNodes[0].text;
                                entRef.logicalName = attr.childNodes[k].childNodes[1].childNodes[1].text;
                                entRef.name = attr.childNodes[k].childNodes[1].childNodes[2].text;
                                obj[sKey] = entRef;
                                break;
                           
                            default:
                                var entCV = new jsCrmValue();
                                entCV.type = sType;
                                entCV.value = attr.childNodes[k].childNodes[1].text;
                                obj[sKey] = entCV;
                           
                                break;
                        }
                    
                    }

                    jDE.attributes = obj;
                    break;
                   
                case "Id":
                    jDE.guid = oResultNode.childNodes[j].text;
                    break;
                   
                case "LogicalName":
                    jDE.logicalName = oResultNode.childNodes[j].text;
                    break;
                   
                case "FormattedValues":
                    var foVal = oResultNode.childNodes[j];
                   
                    for ( var k = 0; k < foVal.childNodes.length; k++){
                        // Establish the Key, we are going to fill in the formatted value of the already found attribute
                        var sKey = foVal.childNodes[k].firstChild.text;                   
               
                        jDE.attributes[sKey].formattedValue = foVal.childNodes[k].childNodes[1].text;
                       
                       
                    }
                    break;
            }
            
        }
       
        results[i] = jDE;
    }    
    
   

    //return entities
    if (callback != null)
        callback(results);
    else
        return results;
       
}  

Finally we need to add a few JavaScript objects (these can be added anywhere within your FetchUtil.js file).

function jsDynamicEntity(gID, sLogicalName)
{
    this.guid = gID;
    this.logicalName = sLogicalName;
    this.attributes = new Object();
}
 
function jsCrmValue(sType,sValue)
{
    this.type = sType;
    this.value = sValue;   
}

function jsEntityReference(gID, sLogicalName, sName){
    this.guid = gID;
    this.logicalName = sLogicalName;
    this.name = sName;
    this.type = 'EntityReference';
}

function jsOptionSetValue(iValue, sFormattedValue){
    this.value = iValue;
    this.formattedValue = sFormattedValue;
    this.type = 'OptionSetValue';
}

 

Summary

The SOAP service for 2011 is quite different, but much improved.  I have found a lot of the 2011 services to be less bloated but more robust and I think this response XML is exactly that.  There are some extra pieces of information in the response XML, so I encourage you to explore it.  Lastly, if you have any feedback or want this to be expanded upon, just post a comment and let me know.  Hope you enjoy!

Post by: Paul Way, Customer Effective

1 thought on “Parsing and Consuming the CRM 2011 SOAP Service Inside JavaScript”

Comments are closed.

Show Buttons
Hide Buttons