Wednesday, March 30, 2011

Microsoft Dynamics CRM 2011: Parsing Jscript with DOM Parser

UPDATE:  please review post here: http://mileyja.blogspot.com/2013/06/format-of-soap-responses-to-microsoft.html as format of the returned response has changed!


Hey gang,

I just wanted to talk through some of the logistics of parsing XML responses when using the CRM 2011 SOAP endpoint in Jscript.

Let's start by examining a piece of XML (that's cut off for the sake length that represents a webresource entity as it is returned to us via a RetreiveMultiple call. For more info on making RetrieveMultiple Call please review this post: http://mileyja.blogspot.com/2011/03/crm-2011-retrievemultiple-calls-in.html

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <ExecuteResponse xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <ExecuteResult i:type="a:RetrieveMultipleResponse" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts">
        <a:ResponseName>RetrieveMultiple</a:ResponseName>
        <a:Results xmlns:b="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
          <a:KeyValuePairOfstringanyType>
            <b:key>EntityCollection</b:key>
            <b:value i:type="a:EntityCollection">
              <a:Entities>
                <a:Entity>
                  <a:Attributes>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>webresourceidunique</b:key>
                      <b:value i:type="c:guid" xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/">340c69f7-3fc7-40e2-ae20-fbab9470d9d4</b:value>
                    </a:KeyValuePairOfstringanyType>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>organizationid</b:key>
                      <b:value i:type="a:EntityReference">
                        <a:Id>a2fe1552-7977-4d92-a730-05eeb167afb5</a:Id>
                        <a:LogicalName>organization</a:LogicalName>
                        <a:Name>crm</a:Name>
                      </b:value>
                    </a:KeyValuePairOfstringanyType>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>displayname</b:key>
                      <b:value i:type="c:string" xmlns:c="http://www.w3.org/2001/XMLSchema">new_webresourceiframe</b:value>
                    </a:KeyValuePairOfstringanyType>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>iscustomizable</b:key>
                      <b:value i:type="a:BooleanManagedProperty">
                        <a:CanBeChanged>true</a:CanBeChanged>
                        <a:ManagedPropertyLogicalName>iscustomizableanddeletable</a:ManagedPropertyLogicalName>
                        <a:Value>true</a:Value>
                      </b:value>
                    </a:KeyValuePairOfstringanyType>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>solutionid</b:key>
                      <b:value i:type="c:guid" xmlns:c="http://schemas.microsoft.com/2003/10/Serialization/">fd140aae-4df4-11dd-bd17-0019b9312238</b:value>
                    </a:KeyValuePairOfstringanyType>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>languagecode</b:key>
                      <b:value i:type="c:int" xmlns:c="http://www.w3.org/2001/XMLSchema">1033</b:value>
                    </a:KeyValuePairOfstringanyType>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>modifiedon</b:key>
                      <b:value i:type="c:dateTime" xmlns:c="http://www.w3.org/2001/XMLSchema">2011-03-20T07:55:52Z</b:value>
                    </a:KeyValuePairOfstringanyType>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>ismanaged</b:key>
                      <b:value i:type="c:boolean" xmlns:c="http://www.w3.org/2001/XMLSchema">false</b:value>
                    </a:KeyValuePairOfstringanyType>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>createdby</b:key>
                      <b:value i:type="a:EntityReference">
                        <a:Id>81ed14d9-4441-e011-963a-080027ad5b6e</a:Id>
                        <a:LogicalName>systemuser</a:LogicalName>
                        <a:Name i:nil="true" />
                      </b:value>
                    </a:KeyValuePairOfstringanyType>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>webresourcetype</b:key>
                      <b:value i:type="a:OptionSetValue">
                        <a:Value>1</a:Value>
                      </b:value>
                    </a:KeyValuePairOfstringanyType>
                    <a:KeyValuePairOfstringanyType>
                      <b:key>canbedeleted</b:key>
                      <b:value i:type="a:BooleanManagedProperty">
                        <a:CanBeChanged>true</a:CanBeChanged>
                        <a:ManagedPropertyLogicalName>canbedeleted</a:ManagedPropertyLogicalName>
                        <a:Value>true</a:Value>
                      </b:value>

Now let's look at how this might be parsed. In CRM 4.0 each attribute had it's own matching tag name so you could just search for the tag and grab the value using the DOM parsar. That is not the case here as each attribute is wrapped in a then the first child node contains the name of the attribute. The following child nodes contain the data associated with the attribute. For an ntext or a number field you will only have two total subkeys, one for the attribute name and one for the value.

This means we can parse out most simple types above using a function like this:


function parseResponse(responseXML, attributename) {

    debugger;
    xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
    xmlDoc.async="false";
    xmlDoc.loadXML(responseXML);
    x=xmlDoc.getElementsByTagName("a:KeyValuePairOfstringanyType");
    for (i=0;i<x.length;i++)
   {
      if (x[i].childNodes[0].text == attributename)
      {
         //we decode the base 64 contents and alert the HTML of the Iframe
          alert(x[i].childNodes[1].text);
      }
      
   } 
}

Just pass in the xml from the response containing your entity and your attribute you are looking for.

This function iterates through the attributes by grabbing a collection of attributes and walking through them, searches for our "attributename" in the first child node, and if it's found, alerts our second child node that represents the value for the attribute.

You will notice that in the XML that there are exceptions to this rule. When it comes to a lookup field (called an "entityreference") type above you have info representing ID (GUID ID Value), Logical name (Type of entity), and value(usually the name attribute of that entity). You also have attributes of type "BooleanManagedProperty" where you have a "CanBeChanged" child node in between the name of the attribute an the value. I am sure there are more examples but here is how to tackle this.

You will notice in the first line of the of the Jscript above I included this miscellaneous line: debugger;

This causes a breakpoint in your code that can be picked up in your code as long as you go in IE to Tools - Internet Options - Advanced and uncheck the two "Disable script debugging" check boxes that are checked by default in IE.

Now, when IE hits the break point it will ask you how you want to debug it. I use Visual Studio 2010 for this debugging, and it stops on my breakpoint and I can use F10 and F11 to step through or step into my code step by step. Then I can add a watch or whatever I want. In this case I am going to add a watch on "x[i]" because it represents my attributes from our line, x=xmlDoc.getElementsByTagName("a:KeyValuePairOfstringanyType"); as I step through.

Now, if I step into the code in Visual Studio and add my watch I can see all the attributes and can clearly see that I can look at the text of the first child node to see my attribute value and I can also drill down into the child nodes and other collections and properties within the structure to determine the syntax I need to unlock the needed values of the other types.


You can see above I am looking at the [0] child node and that the text is "content", you could look at the [1] child node here and see that it contains the value in the text property.

If you have questions feel free to ask questions in the comments.  I am more than willing to help with this kind of thing.

I hope this helps!

4 comments:

  1. Hi Jamie,

    If more than 1 record retrieved, how can I get the values of the other records?

    Because if I use the parseResponse it just returns the first value of the attribute.

    Thanks in advance.

    ReplyDelete
  2. Jamie,

    How do you process a retrieve response?

    ReplyDelete
    Replies
    1. If you need the response you might be better off with a synchronous retrievemultiple call. There is an example here:

      http://mileyja.blogspot.com/2011/07/using-jscript-to-access-soap-web.html

      Delete