CRM 2011 Javascript Fetch XML synchronous or asynchronous call

Using helper function to execute FetchXML request is great.

The code below is retrieved from this blog, and all I had to do is to add extra logic when parsing the XML result because I want to grab the attributes from the related entity as well.

Modified FetchUtil.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/// <summary>FetchUtil.js</summary>
var XMLHTTPSUCCESS = 200;
var XMLHTTPREADY = 4;
 
function FetchUtil(sOrg, sServer) {
    this.org = sOrg;
    this.server = sServer;
 
    if (sOrg == null) {
        if (typeof (ORG_UNIQUE_NAME) != "undefined") {
            this.org = ORG_UNIQUE_NAME;
        }
    }
 
    if (sServer == null) {
        this.server = window.location.protocol + "//" + window.location.host;
    }
}
 
FetchUtil.prototype._ExecuteRequest = function (sXml, sMessage, fInternalCallback, fUserCallback) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("POST", this.server + "/XRMServices/2011/Organization.svc/web", (fUserCallback != null));
    xmlhttp.setRequestHeader("Accept", "application/xml, text/xml, */*");
    xmlhttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
 
    if (fUserCallback != null) {
        //asynchronous: register callback function, then send the request.
        var crmServiceObject = this;
        xmlhttp.onreadystatechange = function () { fInternalCallback.call(crmServiceObject, xmlhttp, fUserCallback) };
        xmlhttp.send(sXml);
    }
    else {
        //synchronous: send request, then call the callback function directly
        xmlhttp.send(sXml);
        return fInternalCallback.call(this, xmlhttp, null);
    }
}
 
FetchUtil.prototype._HandleErrors = function (xmlhttp) {
    /// <summary>(private) Handles xmlhttp errors</summary>
    if (xmlhttp.status != XMLHTTPSUCCESS) {
        var sError = "Error: " + xmlhttp.responseText + " " + xmlhttp.statusText;
        alert(sError);
        return true;
    } else {
        return false;
    }
}
 
FetchUtil.prototype.Fetch = function (sFetchXml, fCallback) {
    /// <summary>Execute a FetchXml request. (result is the response XML)</summary>
    /// <param name="sFetchXml">fetchxml string</param>    /// <param name="fCallback" optional="true" type="function">(Optional) Async callback function if specified. If left null, function is synchronous </param>
 
    var request = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
    request += "<s:Body>";
 
    request += '<Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services">' +
'<request i:type="b:RetrieveMultipleRequest" ' +
'<b:KeyValuePairOfstringanyType>' +
'<c:key>Query</c:key>' +
'<c:value i:type="b:FetchExpression">' +
'<b:Query>';
 
    request += CrmEncodeDecode.CrmXmlEncode(sFetchXml);
 
    request += '</b:Query>' +
'</c:value>' +
'</b:KeyValuePairOfstringanyType>' +
'</b:Parameters>' +
'<b:RequestId i:nil="true"/>' +
'<b:RequestName>RetrieveMultiple</b:RequestName>' +
'</request>' +
'</Execute>';
 
    request += '</s:Body></s:Envelope>';
 
    return this._ExecuteRequest(request, "Fetch", this._FetchCallback, fCallback);
}
 
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:AliasedValue":
                                var entRef = new jsEntityReference();
                                entRef.guid = attr.childNodes[k].childNodes[1].childNodes[2].childNodes[0].text;
                                entRef.logicalName = attr.childNodes[k].childNodes[1].childNodes[2].childNodes[1].text;
                                entRef.name = attr.childNodes[k].childNodes[1].childNodes[2].childNodes[2].text;
                                obj[attr.childNodes[k].childNodes[1].childNodes[0].text] = entRef;
                                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;
 
}
 
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';
}
 
//setting CRM fields
function SetLookupField(res, fieldName) {
    if (res[0].attributes[fieldName] != null) {
        SetLookupValue(fieldName, res[0].attributes[fieldName].id, res[0].attributes[fieldName].name, res[0].attributes[fieldName].logicalName);
    }
}
 
function SetNumberField(res, fieldName) {
    if (res[0].attributes[fieldName] != null) {
        Xrm.Page.getAttribute(fieldName).setValue(parseFloat(res[0].attributes[fieldName].value));
    }
}
 
function SetLookupValue(fieldName, id, name, entityType) {
    if (fieldName != null) {
        var lookupValue = new Array();
        lookupValue[0] = new Object();
        lookupValue[0].id = id;
        lookupValue[0].name = name;
        lookupValue[0].entityType = entityType;
 
        Xrm.Page.getAttribute(fieldName).setValue(lookupValue);
    }
}


To use it you just need to do it like this (synchronous)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function FetchData(guid) {
    var _oService;
    var _sOrgName = "";
    var _sServerUrl = Xrm.Page.context.getServerUrl();
    var sFetch = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>" +
  "<entity name='new_productiongrowerforecast'>" +
  "<attribute name='new_name' />" +
  "<attribute name='new_commodityid' />" +
  "<attribute name='new_potentialtonnage' />" +
  "<attribute name='new_productiongrowerforecastid' />" +
  "<order attribute='new_name' descending='false' />" +
  "<filter type='and'>" +
  "<condition attribute='new_productiongrowerforecastid' operator='eq'   value='" + guid + "' />" +
  "</filter>" +
  "<link-entity name='new_productionforecast' from='new_productionforecastid' to='new_productionforecastid' visible='false' link-type='outer' alias='pfdpf'>" +
  "<attribute name='new_seasonid' />" +
  "<attribute name='new_growerid' />" +
  "</link-entity>" +
  "</entity>" +
  "</fetch>"
 
    _oService = new FetchUtil(_sOrgName, _sServerUrl);
    var res = _oService.Fetch(sFetch);
}


If it's asynchronous the last bit will be:
1
2
var res = _oService.Fetch(sFetch, callBackFunction);
//implement your own callBackFunction here


The parsed result object can be queried like this:
1
2
var seasonId = res[0].attributes["new_seasonid"].guid;
var originalTonnage = res[0].attributes["new_potentialtonnage"].value;


Hope this helps,
Andreas

Comments

  1. Hi...

    Thanks a lot for share your knowledge with us. Okay, while this method does enable you to execute a FetchXML request in JavaScript against the 2011 SOAP endpoint, it doesn't return XML in the same format as the 4.0 web service did.

    Keep sharing.

    ReplyDelete
  2. hey it works on me but when i try to save the form it gives an error message:

    “An Error has occurred. Try this action again. If the problem continues, check the Microosft Dynamics CRM Community for solutions or contact your Microsoft Dynamics administrator. Finally, you can contact Microsoft Support.”

    can u help me?

    ReplyDelete
  3. Hi Ahmet, what happens after you click ok to that message?
    I believe that error message is not related to this as it doesn't look like a JS error.

    ReplyDelete
  4. I got error at request += CrmEncodeDecode.CrmXmlEncode(sFetchXml); this line

    CrmEncodeDecode is not defined

    ReplyDelete
    Replies
    1. Hi m2012,

      Are you running this outside CRM?
      You might want to check this out:
      http://mscrmkb.blogspot.com.au/2011/12/crm-2011-javascript-giving-error.html

      Delete
  5. Andreas

    Where did you get the information for the a:AliasedValue from - the code is failing for me as all that seems to be there is the name which is in attr.childNodes[k].childNodes[1].childNodes[2].childNodes[0].text; and the other childnodes are not there (e.g. attr.childNodes[k].childNodes[1].childNodes[2].childNodes[1].text;) making the code fail?

    I am reading the parent account name from the contact

    Andrew

    ReplyDelete
    Replies
    1. Hi,

      Sorry I can't remember what I used AliasedValue for - but I needed it that's why I put it there I believe. If you don't need those attributes feel free to remove it to make it work on your end :D

      Delete
  6. Hi
    Thanks for sharing. I'm getting "Access is deined" error. Can some one guide me how to avoid this.

    ReplyDelete
    Replies
    1. Are you a system administrator? it might be entity permission issue.

      Delete

Post a Comment

Popular posts from this blog

SharePoint 2013 anonymous access add attachments to list item

CRM Plugin - Parent and Child Pipeline