PowerShellASP Applied: JSON Factory

Now that we can use PowerShellASP with SharePoint, what can we do with it? Well without master page or web part support, we can’t easily integrate PoShASP content directly into the SharePoint experience. Fortunately, we can leverage PowerShell in other ways. An easy example is generating JSON, perhaps from the PowerShell standby Get-Process:

<% $Response.ContentType = "application/json; charset=utf-8" %>
<% $Response.Cache.SetCacheability([System.Web.HttpCacheability]'NoCache') %>
<% $Response.Cache.SetExpires([DateTime]::MinValue) %>
<% if( !($n = $Request.QueryString["n"] -as [int]) ) { $n = 10 } %>
{"processes":[
  <% [string]::Join(",`n  ",
  (get-process | sort ws -desc | select -first $n | foreach {
  "{'ID':$($_.ID),'Name':'$($_.Name)','WS':$($_.ws)}" })) %>
]}

After we set the JSON ContentType and cache policy, we try to fetch an “n” query parameter. If n is missing or not a number, we default to 10. Now we can start building our JSON object. To build our array, we use System.String‘s static Join method (the backtick is PowerShell’s escape character, so `n is a newline). Join‘s string[] parameter is provided by a PowerShell pipeline that fetches our system processes, sorts by working set size, selects the top $n and then iterates through those process objects, returning a string of JSON with our desired properties. Note also that PoShASP seems to require an empty line at the end of the file, otherwise “]}” is ignored. Our output will look something like this:

{"processes":[
  {'ID':1672,'Name':'sqlservr','WS':506028032},
  {'ID':3296,'Name':'devenv','WS':230555648},
  {'ID':4720,'Name':'w3wp','WS':181100544},
  {'ID':376,'Name':'services','WS':121995264},
  {'ID':3372,'Name':'w3wp','WS':102522880}
]}

Let’s save our JSON endpoint as %12%\TEMPLATE\LAYOUTS\Get-Process.json.ps1x, which we can test by visiting http://server/_layouts/Get-Process.json.ps1x?n=5. Now it’s AJAX time! Since not everyone can use the ASP.NET AJAX Extensions, we’ll just do it the old-fashioned way. Starting with a Content Editor Web Part on a page of your choice, set the source to the following:

<div id="d_procs">
<img src="/_layouts/images/GEARS_AN.GIF"
  alt="Loading..." align="center" />
</div>
<script>
function getXmlHttpRequestObject() {
 if (window.XMLHttpRequest) {
  return new XMLHttpRequest();
 } else if(window.ActiveXObject) {
  return new ActiveXObject("Microsoft.XMLHTTP");
 } else {
  document.getElementById('d_procs').innerHTML =
  'Cound not create XmlHttpRequest Object.';
 }
}

var r = getXmlHttpRequestObject();
var c = 0;
var mTimer;

function getProcs() {
 if (r.readyState == 4 || r.readyState == 0) {
  r.open("GET", '/_layouts/Get-Process.json.ps1x?n=5', true);
  r.onreadystatechange = handleReceiveProcs;
  r.send(null);
 }
}

function handleReceiveProcs() {
 if (r.readyState == 4) {
  var d = document.getElementById('d_procs');
  var response = eval("(" + r.responseText + ")");
  var p = response.processes;
  var t = "<table width=\"100%\">";
  t += "<tr><th>ID</th><th>Name</th><th>WS(MB)</th></tr>";
  for(i=0;i < p.length; i++) {
   t += '<tr><td>'+p[i]['ID']+'</td>';
   t += '<td>'+p[i]['Name']+'</td/>';
   t += '<td align=\"right\">';
   t += (p[i]['WS']/(1024*1024.0)).toFixed(2);
   t += '</td/></tr>';
  }
  t += "</table><p>Refresh count: "+(++c)+"</p>";
  d.innerHTML = t;
  mTimer = setTimeout('getProcs();', 3000); // 3-sec refresh
 }
}

_spBodyOnLoadFunctionNames.push('getProcs');
</script>

I haven’t put much effort into cleaning up the code, but it does the job (in FF2 and IE7, at least):

With PowerShell’s ease of development and specialized adapters, this is just the tip of the iceberg. What other real-time data would you find useful?

Advertisements