JQuery for Everyone: Get XML List Data with OWSSVR.DLL (RPC)
Web services are hot. I may write a jQuery plugin to talk to SP web services at some point, but first I wanted to do something novel, write a JavaScript tool that can fetch list data using the RPC method (specifically the URL protocol).
Who Wants This?
Anyone developing solutions around List View web parts may find this helpful. A common request from users involves performing various math functions on calculated columns. OOB, SP does not do this which lead me to develop code like Total Calculated Columns.
Better Than DOM Walking?
The DOM walking technique I used previously works but has limitations. Pulling data out of the rendered HTML comes with a set of problems. First, since we read the DOM for data, we can only include rendered records. Records inside closed groups have not loaded and therefore cause errors in the calculations. Second, these scripts are fragile because the DOM can change. Users often have the ability to move (or remove) columns.
Better Than Web Services?
I would argue that the RPC method performs better than web services, but I don’t have any data to support that (yet). I know that the server caches responses to RPC requests and the web parts on the page have already performed the same request. So, we should get a response in the shortest time possible (about 110 ms on my test site).
The power of web services makes some admins nervous. If your server admins have taken precautions to limit or inhibit web services, owssvr.dll will still be an option–it must be because of the way SP works under the hood.
Lastly, through some hacks, owssvr.dll can do some tricks web services can not. For instance, fetching all items in a discussion list (folders/threads and messages/posts), as far as I know, can only be done with the RPC method.
Web services still holds one significant advantage over RPC, the ability to request a list by name. RPC must use the GUID.
Click “Read more…” to get the code and documentation:
Some Documentation
I created several helper functions which are part of the ows object. Each has a usage example in the myCb() callback function at the bottom.

When creating the ows object, the default settings will get the list and view GUIDs from the ctx object on the page. If there is more than one web part, the default settings use the ctx object for the first web part added to the page (not the first appearing in the DOM). To get the correct ctxId for a web part by name, see my other article.
If you specify a custom ctx, you will need to supply the list and view members of the options argument (see last example).
- $.fn.ows(options,callback) – creates the ows object and returns an optional callback function with the modified options argument
- ows.getName(ctx,rsname) – Takes the web part context (can be derived from the options argument if used in the callback) and a column by its display name and returns the ows field name (ows_
) - ows.getNumber(ctx,rsname) – Takes the web part context and a column’s display name and returns the order number assigned to the field in the schema
- ows.getValues(ctx,rsname) – Takes the web part context and a column’s display name and returns defined values found in the ows data
- ows.getAll(ctx,rsname) – Takes the web part context and a column’s display name and returns all values from the ows data
- ows.getSum(ctx,rsname) – Takes the web part context and a column’s display name and returns a sum of numerical, defined data
- ows.getAvg(ctx,rsname) - Takes the web part context and a column’s display name and returns an average of defined values
- ows.getMax(ctx,rsname) – Takes the web part context and a column’s display name and returns the maximum value
- ows.getMin(ctx,rsname) – Takes the web part context and a column’s display name and returns the minimum value
- ows.getTable(ctx) – Takes the web part context and returns the web part’s main table as a jQuery object
- ows.getPosition(ctx,rsname) – Takes the web part context and a column’s display name and returns the order number – 1 (same as number but base 0)
<script type="text/javascript"> //?PageView=Shared&ToolPaneView=2 if(typeof jQuery=="undefined"){ var jQPath="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/"; document.write("<script src='",jQPath,"jquery.min.js' type='text/javascript'><\/script>"); } </script> <script type="text/javascript"> (function(){ Array.max = function(array){ return Math.max.apply( Math, array ); }; Array.min = function(array){ return Math.min.apply( Math, array ); }; var defaults = { ctx: "ctx1", baseUrl: "", list: "", view: "", command: "" }, isNumber = function(val){ return (/^-?((\d+\.?\d*)|(\.\d+))$/).test(val); }, ows_url = function(o){ o.baseUrl = (!o.baseUrl)?L_Menu_BaseUrl:o.baseUrl; o.list = (!o.list)?window[o.ctx].listName:o.list; o.view = (!o.view)?window[o.ctx].view:o.view; return o.baseUrl+"/_vti_bin/owssvr.dll?XMLDATA=1&List="+ o.list+"&View="+o.view+o.command; }, create_ows = function(){ ows = { getName : function(ctx,rsname){ var n = $.grep($(ows[ctx].schema),function(e,i){ return ($(e).attr("rs:name")==rsname); }); return $(n).attr("name"); }, getNumber : function(ctx,rsname){ var r = $.grep($(ows[ctx].schema),function(e,i){ return ($(e).attr("rs:name")==rsname); }); return $(r).attr("rs:number"); }, getPosition : function(ctx,rsname){ var pos = ows.getNumber(ctx,rsname); return parseInt(pos)-1; }, getValues : function(ctx,rsname){ var v = [], vname = ows.getName(ctx,rsname); $(ows[ctx].data).each(function(i,e){ var vx = $(e).attr(vname); if(vx){ v.push(vx); } }); return v; }, getAll : function(ctx,rsname){ var a = [], aname = ows.getName(ctx,rsname); $(ows[ctx].data).each(function(i,e){ a.push($(e).attr(aname)); }); return a; }, getSum : function(ctx,rsname){ var svals = ows.getValues(ctx,rsname), sum=0; $(svals).each(function(i,e){ if(e && isNumber(e)){ sum += parseFloat(e); } }); return sum; }, getAvg : function(ctx,rsname){ var avgsum = ows.getSum(ctx,rsname), avgcount = ows.getValues(ctx,rsname).length; return avgsum/avgcount; }, getMax : function(ctx,rsname){ var xvals = ows.getValues(ctx,rsname); xvals = $.grep(xvals,function(e,i){ return (e && isNumber(e)); }); return Array.max(xvals); }, getMin : function(ctx,rsname){ var nvals = ows.getValues(ctx,rsname); nvals = $.grep(nvals,function(e,i){ return (e && isNumber(e)); }); return Array.min(nvals); }, getTable : function(ctx){ var t = $("[id='"+window[ctx].listName+"-"+window[ctx].view+"']"); return t; } }; }; jQuery.fn.ows = function(options,callback){ var o = $.extend({},defaults,options); if(typeof ows=='undefined'){create_ows();} $.get(ows_url(o),function(data){ ows[o.ctx] = { schema:$(data).find("[nodeName='s:AttributeType']"), data:$(data).find("[nodeName='z:row']") }; return callback(o); }); }; })(); $(function(){ var myCb = function(o){ var name = ows.getName(o.ctx,'% Complete'); //console.log(name); var num = ows.getNumber(o.ctx,'% Complete'); //console.log(num); var vals = ows.getValues(o.ctx,'% Complete'); //console.log(vals); var all = ows.getAll(o.ctx,'% Complete'); //console.log(all); var sum = ows.getSum(o.ctx,'% Complete'); //console.log(sum); var count = ows[o.ctx].data.length; //console.log(count); var avg = ows.getAvg(o.ctx,'% Complete'); //console.log(avg); var max = ows.getMax(o.ctx,'% Complete'); //console.log(max); var min = ows.getMin(o.ctx,'% Complete'); //console.log(min); var t = ows.getTable(o.ctx); //console.log(t); var pos = ows.getPosition(o.ctx,'% Complete'); //console.log(pos); }; //this example builds the ows object for the default web part and runs a callback function myCb //$.fn.ows({},myCb); //this example builds the ows object for the default web part //$.fn.ows(); //this example specifies the web part to use and uses an inline callback function $.fn.ows({ctx:'ctx1'},function(o){ console.log(ows.getValues('ctx1','ows_PercentComplete')); }); //this example specifies the list and view which does not need the web part on the page //$.fn.ows({ctx:"custom", list:"{730166FA-800A-4514-8912-AF49C681DF94}", view:"{2AB283B2-7B67-44AF-BC2E-304ACEED2035}"},myCb); }); </script>
Thanks to John Resig for the min/max function idea.
- JQuery for Everyone: Accordion Left Nav
- JQuery for Everyone: Print (Any) Web Part
- JQuery for Everyone: HTML Calculated Column
- JQuery for Everyone: Dressing-up Links Pt1
- JQuery for Everyone: Dressing-up Links Pt2
- JQuery for Everyone: Dressing-up Links Pt3
- JQuery for Everyone: Cleaning Windows Pt1
- JQuery for Everyone: Cleaning Windows Pt2
- JQuery for Everyone: Fixing the Gantt View
- JQuery for Everyone: Dynamically Sizing Excel Web Parts
- JQuery for Everyone: Manually Resizing Web Parts
- JQuery for Everyone: Total Calculated Columns
- JQuery for Everyone: Total of Time Differences
- JQuery for Everyone: Fixing Configured Web Part Height
- JQuery for Everyone: Expand/Collapse All Groups
- JQuery for Everyone: Preview Pane for Multiple Lists
- JQuery for Everyone: Preview Pane for Calendar View
- JQuery for Everyone: Degrading Dynamic Script Loader
- JQuery for Everyone: Force Checkout
- JQuery for Everyone: Replacing [Today]
- JQuery for Everyone: Whether They Want It Or Not
- JQuery for Everyone: Linking the Attachment Icon
- JQuery for Everyone: Aspect-Oriented Programming with jQuery
- JQuery for Everyone: AOP in Action - loadTip Gone Wild
- JQuery for Everyone: Wiki Outbound Links
- JQuery for Everyone: Collapse Text in List View
- JQuery for Everyone: AOP in Action - Clone List Header
- JQuery for Everyone: $.grep and calcHTML Revisited
- JQuery for Everyone: Evolution of the Preview
- JQuery for Everyone: Create a Client-Side Object Model
- JQuery for Everyone: Print (Any) Web Part(s) Plugin
- JQuery for Everyone: Minimal AOP and Elegant Modularity
- JQuery for Everyone: Cookies and Plugins
- JQuery for Everyone: Live Events vs. AOP
- JQuery for Everyone: Live Preview Pane
- JQuery for Everyone: Pre-populate Form Fields
- JQuery for Everyone: Get XML List Data with OWSSVR.DLL (RPC)
- Use Firebug in IE
- JQuery for Everyone: Extending OWS API for Calculated Columns
- JQuery for Everyone: Accordion Left-nav with Cookies Speed Test
- JQuery for Everyone: Email a List of People with OWS
- JQuery for Everyone: Faster than Document.Ready
- jQuery for Everyone: Collapse or Prepopulate Form Fields
- jQuery for Everyone: Hourly Summary Web Part
- jQuery for Everyone: "Read More..." On a Blog Site
- jQuery for Everyone: Slick Speed Test
- jQuery for Everyone: The SharePoint Game Changer
- JQuery For Everyone: Live LoadTip
Paul, I see that you mention caching in your post. Have you experienced issues where the list is updated but because of the cache the browser still returns the old result? Is there any way to tune the cache behavior for all users?
@Christophe,
Only IE8 seems to have this problem, but it can be reversed by changing from .get() to .ajax(). Using the AJAX method, set the cache option to false. This appends a random query string to the request so the browser will not cache it.
I think I’ve seen this issue in previous versions of IE…well, nevermind.
Thanks for reminding me of the tip about random querystrings :-) (I didn’t know it was used in jQuery btw).
Interest post but was not able to find the “Click “Read more…” to get the code and documentation:” – where can I see the code. Am especially interested in the call to owssvr.dll.
Thanks,
John
John – Should be visible now. — Mark
Has anyone ever tried to use jQuery to export data to Excel? For example, if I query a list via web service and display it in an HTML table, now, I’d like to export this same data to excel. Any idea on how I could do that?