A use for (and return to) XML responses

JSON has become all the rage, and I have to admit I use it about half the time. The other half of the time I’ve moved into using this technique which I’d like to share. The server side language here is PHP, but the concepts are entirely js centric which is why I’m posting this here and not the PHP forum. The support library of the examples is prototype.js, but jQuery and others are entirely capable of this approach. With those notes let’s begin.

Many, perhaps most, AJAX requests I’ve found consist of two major pieces - a block of HTML to be inserted, and a block of instructions for doing that insertion which may be as simple as calling a single update command or as complex as linking multiple event observers to the newly inserted block. From a “read the code” standpoint I found it annoying to have javascript callback nested in one place and the server code for the generation of that code in another.

Now, if you don’t have any html to insert you can send back the response with “text/javascript” as the header and the prototype.js framework will evaluate the response. This makes the js side very clean.


new Ajax.Request('pathtoserverscript.php?'+$('myform').serialize());

Here we have a form that has fields holding derived data from what the user can input. The fields displaying that data are readonly, and further they aren’t named because for security the server redoes the calculation at submit time. Displaying the derived data is merely a convenience. The server side is also fairly clean

<?php
protected function updateView() {
  $data = $this->tax_rate_service->rateCalculator( $_GET['funds'], $_GET['year'] );
  $responder = new JavascriptResponder();

  ob_start() ?><script type="text/javascript">
    <?php foreach ($data['percentages'] as $fund => $value): ?>
      $('Percentage_<?= $fund ?>').value = '<?= number_format($value, 10).'%' ?>';
    <?php endforeach ?>
    <?php foreach ($data['adaRates'] as $fund => $rate): ?>
      $('ADARate_<?= $fund ?>').value = '<?= $rate ?>';
    <?php endforeach ?>

    $('Percentage').value = '<?= number_format($data['totals']['percentage'], 10).'%' ?>';
    $('CalcRate').value = '<?= $data['totals']['rate'] ?>';

    <?php if( bccomp($data['totals']['rate'], $_GET['rate'], 10) === 0 ): ?>
      $('CalcRate').writeAttribute({'style' : 'color: #000;'});
      $('SaveButton').enable();
    <?php else: ?>
      $('CalcRate').writeAttribute({'style' : 'color: #f00;'});
      $('SaveButton').disable();
    <?php endif ?>
    </script><?php 
		
    $responder->parseScript(ob_get_clean());
    $responder->respond();
}

The JavascriptResponder class has the duty of setting correct headers for a javascript response, it’s parseScript method simply lops off the script tags which are there to get the IDE to color context the js correctly, and no other reason. The result of this approach is our language switches, but our task doesn’t and so the code becomes easier to follow since another file doesn’t have to be looked up.

Also, the client doesn’t have to deal with the callback until it needs it. The server is free to respond however it wishes.

So what if we have an HTML block? Well, we could use the above approach, but js instructions would have to be written to do the inserts. If we send it as json this leads to some escaping and encoding issues which, though solvable, makes the response file hard to read. For this reason I developed this approach which steps back to XML.

First, the javascript side requires small group of functions that’s to be loaded with other persistent libraries the site uses.


var PNL = {
  xmlRequest: function (url, params) {
    if (typeof(params) == 'undefined') {
      params = {};
    }

    params.onComplete = this.parseXMLResponse.bindAsEventListener(this);
    new Ajax.Request(url, params);
  },

  parseXMLResponse: function ( r ) {
    var html = this.getXMLValue(r, 'h');
    var js = this.getXMLValue(r, 'j');

    if (js) {
      eval (js);
    }
  },
	
  getXMLValue: function( node, tag ) {
    try {
      return node.responseXML.getElementsByTagName(tag)[0].firstChild.nodeValue;
    } catch (e) {}
  }
}

Now with that in place setting up a callback for an event becomes as simple as this.


new PNL.xmlRequest('pathtoserverfile.php');

The server composes an xml file with two blocks, both enclosed in CData


<r>
  <j><![CDATA[ 
  .. javascript here ..
   ]]></j>
  <h><![CDATA[ 
  .. html to insert here ..
   ]]></h>
</r>

The server response ends up like this

<?php
$responder = new XMLResponder('htmltemplatename' );
$responder['ada'] = $data;

ob_start() ?><script type="text/javascript">
  $('Breakdowns').insert({bottom: html});
  $$('.ADA input.Amounts').invoke('observe', 'change', updateTotals );

  $('ADADelete').observe('click', function(ev){ 
    $$('.ADA').invoke('remove');
    updateTotals();
  });

  if (!$('BreakdownTable').visible()) {
    $('BreakdownTable').show();
  }
			
</script><?php 
		
$responder->parseScript(ob_get_clean());
$responder->respond();

The XMLResponder extends off the php side’s template parser. This allows the templates used for composing this javascript response to also be used to assemble the page itself on page reload, or if ajax isn’t present. The details of that are out of the scope of the post - the point of the example is to again show how the thought process for writing the a fairly complex callback can be streamlined.

I’m guessing that there might be a way to do this in json, but I don’t think json can do it in fewer characters. The xml markup is very compressed, and any json block would have to have a LOT of character escaping going on the moment the HTML gets complicated. Debugging the response block is made painful by that escaping so I would rather not deal with it.

BTW, this technique also presents a rare example of a properly used eval statement.

I’ve found that letting the server choose the callback arbitrarily rather than try to pick from a menu of preloaded, precanned callbacks is very powerful and flexible. It also helps with planning since you don’t have to worry with a callback until you actually write it.