Getting Started With jQuery - Advanced Ajax To The Client
Written by Ian Elliot   
Monday, 12 October 2015
Article Index
Getting Started With jQuery - Advanced Ajax To The Client
The Standard DataTypes
Type Converters
The dataFilter function

Type Converters

When you specify one of the data types in dataType jQuery invokes a converter. A converter is a function that takes one data type and turns it into another. At its simplest you can specify a single built in data type and jQuery will call a single converter function.

For example when you do a request for a script data type to the same domain jQuery downloads the script using the standard Ajax request as text and then automatically invokes the converter function:

function(text){
 jQuery.globalEval(text);
 return text;
}

i.e. a text to script converter.

If you want to do something more complex you can actually specify a set of data types and jQuery will attempt to perform each data conversion in turn. For example

options.dataType="json text xml";

causes jQuery to first convert to json, then to text and then to xml. This isn't particularly realistic but it conveys the principle. 

The next piece of information you need to know is that all Ajax requests return text to the client and it is jQuery that does the conversion to what ever the Content-Type header or the dataType specified. For example if the Content-Type header is "application/json" or if you set dataType to json then the first converter to be called is text to json. 

The general principle is that either the inferred or specified data type makes jQuery call the appropriate converter function. 

converters

You can specify your own converter functions using the converters option. This is set to an object of key value pairs. The key is the name of the data type to convert between  and the value is the conversion function. By default it is set to:

{"* text": window.String, "text html": true,
  "text json": jQuery.parseJSON,
  "text xml": jQuery.parseXML}

any key value pairs that you set via the converters option are added to the defaults - but you can override them. The converter function that you specify has to accept the raw data from the Ajax request and return the processed data.  

The best way to understand converters is to first override an existing converter function. As an example let's add date handling to JSON which is a very common real application of converters. 

When you convert an object to a JSON string the conversion handles strings, arrays, numeric values and so on but it doesn't handle dates. A date object simply gets converted to a date string and when jQuery does an automatic conversion the date field is just a string. 

For example:

<?php
header("Content-Type:application/json;
                         charset=UTF-8");
$cdata='{"first": "Ian","last":"Elliot",
          "bday":"2013-10-21T13:28:06.419Z"}';
echo($cdata);
?>

where the JSON string being sent has a final property bday that is the standard JavaScript string representation of a date/time.

For example if you do:

var date = new Date();
var json = JSON.stringify(date);

then json will contain something like the string "2013-10-21T13:28:06.419Z".

When you ask for this JSON data using the usual:

var options={};
options.url="process.php";
options.method="get";
$.ajax(options).then(
 function(data,test,XHR){
  console.log(data);
 });

then the data will contain an object:

Object { first: "Ian", last: "Elliot",
          bday: "2013-10-21T13:28:06.419Z" }

Notice that the bday property is set to a string not a Date object.

If you want to convert bday to a Date object automatically then you can do it by overriding the text to json converter. All you have to do is set the converters property accordingly:

var options={};
options.url="process.php";
options.method="get";
options.converters={
  "text json":function(data){
    data=JSON.parse(data);
    data.bday=new Date(data.bday);
    return data;
  }};


$.ajax(options).then(
 function(data){
  console.log(data);
 });

Notice that the custom converter's key is "text json". Which means it will be called when ever the Content-Type header or the dataType is json to convert the raw text to JSON. The actual function doesn't do a great deal. It gets the raw text in its data parameter and then uses the JSON parser to convert the text to JSON but with bday set to a String. To convert this to a Date we simply create a new Date object using the date string to initialize it. If you try this out you will see:

Object { first: "Ian", last: "Elliot",
         bday: Date 2013-10-21T13:28:06.419Z }

You can see that the bday property is now a Date object set to the correct date.

Custom Types And Converters

As well as overriding existing data types you can introduce your own. 

For example in the case of JSON with dates it is probably a good idea to distinguish your custom JSON format from all other JSON formats. Let's call your particular JSON with data mytype.

What we need now is a converter from text to mytype and this is just the converter we already have but with text mytype as the key:

options.converters={"text mytype":
 function(data){
  data=JSON.parse(data);
  data.bday=new Date(data.bday);
  return data;
 }};

Of course if the server sends the standard json header our converter will not be called so we need to either change the header or set the dataType to mytype. If you try the program with:

options.dataType="mytype";

You will discover that it works perfectly and the converter is called to process the text to mytype conversion even though the server says its just plain ordinary json within its header.

However if you get the server to send an appropriate header:

<?php
header("Content-Type:mytype; charset=UTF-8");
$cdata='{"first": "Ian","last":"Elliot",
         "bday":"2013-10-21T13:28:06.419Z"}';
echo($cdata);
?>

You will discover that nothing happens and your custom converter from text to mytype isn't called.

The reason is that mytype is the internal jQuery name for the data type and not the Content-type header that identifies the data type. For example, internally we use the jQuery data type json, but the header that is sent is application/json. Clearly we need to connect the internal name to the header and this is where  the mysterious contents option comes into play.

contents

A set of key value pairs. The key is the internal jQuery data type name and the value is a regular expression that matches the header of the type.

So if we want a header that contains mytype to map to the internal jQuery mytype we have to add:

options.contents={mytype:/mytype/};

Now everything works and the convert is called if the header contains the string mytype. 

Notice that you don't have to use the same internal type name and header type name. For example you could decide to call your data type jsond - for json with dates - and in this case the contents option would be:

options.contents={jsond:/mytype/};

This works but if you go to the next step and decide that you really want the header to be jsond as well then you need to use:

options.contents={jsond:/jsond/};

and you will find that everything stops working. 

The reason is very simple. The default for contents is:

contents: { xml: /xml/,
           html: /html/, 
           json: /json/ }

and your key value pair is added to the end of the default list. You can guess what happens. When the server sends a header with jsond in it the entry json:/json/ matches before your jsond:/jsond/ does and so your custom converter never gets called.

There are two possible solutions. Don't call your data type anything like xml, html or json or modify the definition of json.

For example:

options.contents=
    {jsond:/jsond/,json:/json(?!d)/};

This redefines the mapping of json to "json" not followed by "d". In other words the regular expression for json has been modified to only match anything containing "json" but not if it is "jsond". If you now try the program you will find it all works:

<?php
header("Content-Type:jsond; charset=UTF-8");
$cdata='{"first": "Ian","last":"Elliot",
        "bday":"2013-10-21T13:28:06.419Z"}';
echo($cdata);
?>

var options={};
options.url="process.php";
options.method="get";
options.contents=
     {jsond:/jsond/,json:/json(?!d)/};
options.converters={"text jsond":
 function(data){
  data=JSON.parse(data);
  data.bday=new Date(data.bday);
  return data;
 }};

$.ajax(options).then(
 function(data){
  console.log(data);
 });

Of course you can still override the header using the dataType option. 



Last Updated ( Thursday, 05 May 2022 )