To install the plugin, simply paste this code into a .js file and load it after loading jquery.js:

// JSON for jQuery by Michael Geary

// See http://mg.to/2006/01/25/json-for-jquery

// Free beer and free speech. Enjoy!

$.json = { callbacks: {}};

$.fn.json = function( url, callback ){

var _$_ = this;

load( url.replace(/{callback}/, name(callback)));

returnthis;

functionname( callback ){

var id = (new Date).getTime();

varname = 'json_' + id;

var cb = $.json.callbacks[id] = function( json ){

delete $.json.callbacks[id];

eval('delete ' + name);

_$_.each(function(){ callback(json); });

};

eval(name + ' = cb');

returnname;

}

function load( url ){

var script = document.createElement('script');

script.type = 'text/javascript';

script.src = url;

$('head',document).append( script );

}

};

This adds a json() method to the $ function. The first argument is the URL to the JSON resource, with the text {callback} wherever the JSON callback method should be provided. In a JSONP URL, you would use jsonp={callback}; in a Yahoo! JSON URL you would use format=json&callback={callback}.

The second argument is the callback function itself. When the JSON resource finishes loading, this function will be called with a single argument, the JSON object itself. Inside the callback function, this is a reference to the HTML element found by the $ function. (If $ found more than one element, the callback function is called for each of them.)

The callback function is required, so this code won’t work with plain JSON APIs like del.icio.us that don’t let you specify a callback function. This would be easy enough to fix; I didn’t need it for the code I was writing, and didn’t think of it until just now. :-)

The code goes to a bit of extra work to create both an array entry and a unique global name for each callback. The global name is what is substituted into the {callback} part of the URL. It uses this name instead of the array reference to ensure compatibility with any JSON APIs that don’t allow special characters in the callback name. In fact, in the current code the callbacks[] array entries are not really used, but I figured it could be handy to have an array of all outstanding callbacks.

Update: John Resig suggested a couple of improvements to the code, so it’s updated, simpler and better now.

Update 2: Code updated to include Stephen and Brent’s fixes from the comments.

Thanks, Ashutosh. The code syntax coloring uses GeSHi, the Generic Syntax Highlighter. I modified an existing Drupal plugin to make better use of this code formatter, and I’ve been meaning to post it on the Drupal site or here. Sorry about that, I must take care of it.

I would also recommend lightweight syntex highlighter called “prettyprint” used on Google Code. It is lightweight and looks very clean.

Regarding this post. I came across it while searching for RSS to JSON converter plugin for jQuery. I thought I would share it here with everybody. The great thing about it is that you don’t need to setup any server-side scripts. All you need is a jQuery.js file. Everything is done on client side leveraging Google Feeds API.

We’re in the process of building JSON interfaces to our services. We’ve found a serious problem using this particular technique of dynamically adding script tags to the document head: IE leaks memory like a sieve when you do this. According to some MSDN documentation I read, they refuse to garbage collect script text on a page until the page is destroyed.

Given that we routinely send hundreds of kilobytes of JSON data across the channel, this became a problem for us very quickly. The solution is to wrap the script call inside a dynamically-generated iframe element.

Both functions let you download JSON data, but they use different techniques and are made for different situations.

$.json() (this plugin) uses a dynamic script tag to download the JSON data. This lets it work across domains, but it requires that the JSON data be wrapped in a callback function (JSONP format).

$.getJSON() is an AJAX call, that is, it uses XMLHttpRequest. So it works only within the same domain—you can’t use it for cross-domain requests. But it doesn’t require JSONP format; it will work with any standard JSON data.

I looked at the jQuery sources in the mean time. To me, jQuerys $.ajax seems to be lacking a fallback mechanism in case ActiveX controls are disabled. This is where your solution — or Ralf Engelschalls — could help.

I added your code to my js files and put in the right order. I am new at this so I am trying to learn. When I runt he following code. I get an javascript error on my page and no output. Can you help me with what I am doing wrong. I am really trying to understand this stuff. Thanks.

I added a log() function to replace the alerts. If you load the page in Firefox with Firebug enabled, it will use console.debug() to log the messages, including making objects like the json response clickable and browsable. If Firebug is not available, it falls back to an alert() call. The fancy .apply() stuff is to make multiple arguments work in both these cases.

There were two things wrong in the code. First, you need to call the json function with $(selector).json(), where selector is an optional jQuery selector. You don’t need any selector in this particular case, so I left it empty. (In hindsight, my requirement for $() with or without the selector was a bit goofy—I will probably update the code sometime to make it work the way you tried to use it.)

After I fixed that, though, it still didn’t get to the callback function with the “json received” message. So I tried opening your Flickr feed URL directly in a browser, and found that the JSON response calls a function named jsonFlickrFeed(). I changed the {callback} to MikeTest, so the URL ended with format=json&jsonp=MikeTest. I also tried changing it to format=json&jsoncallback=MikeTest as described on Flickr’s JSON API page, but the response still called jsonFlickrFeed() instead of MikeTest().

It appears that Flickr’s feed API doesn’t support the user-defined callback function name that this plugin normally requres. (Their REST API does support jsoncallback=functionName and works fine with the plugin.)

A quick workaround for that is to simply define the jsonFlickrFeed() function directly instead of specifying the callback function in the $().json() call. At this point the plugin isn’t doing much for you except for creating the dynamic script tag, but it does get things working.

When I get a chance I’ll update the plugin to support this more directly. And hopefully Flickr will update their feed API to support the user-defined callback names as their REST API does.

Wow, all I have to say is THANK YOU. I really appreciate the time you took to help me understand this. I will be working on this today after I pick through your code you gave me. Thank you for teaching me the log().

Be sure to load your page with Firebug and watch the console log - it’s pretty cool to see your name show up in the JSON response.

Also, install the Firefox Web Developer toolbar if you don’t already have it. Load your page and then select Information/View JavaScript from the toolbar. A new window or tab will open showing all of the scripts loaded in the page—inline, .js files, and JSON downloads. Click the Collapse All link at the top of the page to see just the URLs. Very handy to check out your JSON downloads this way.

Matthew, it sounds like you are running into cross-domain security. If your page is loaded from a domain other than “sub.domain.tld” then $.ajax will fail.

That is the whole point of the JSONP format with dynamic script tags as used in this plugin—it avoids all of the cross-domain security issues. You can load a script tag from any domain, and JSONP format is simply a way to request that a specific callback function name be executed with the JSON data as an argument.

Does your server actually provide JSONP output, where you can specify the callback function name in the URL? If it does, then you can use the plugin directly and not use $.ajax at all.

Excellent plugin first off. I love your closure solution for queuing the callbacks. I’ve actually used the idea of it for a recent plugin to use the Del.icio.us json feeds. I had to modify it to use an auto-incrementing array to house the callback references instead of the date-string based hash object due to the need to call several scripts in rapid succession. I was seeing callbacks overwritten due to them getting the exact same date string (I was surprised).

In any case… The main reason for my comment is to point out a problem I just fixed in my plugin and which your code appears to share. jQuery 1.1.4 now detects script elements being appended to the DOM using the $().append() method. When it detects one that also has a src attribute, it attempts to use $.ajax to grab the script and execute it. Our code will, for obvious reasons, cause that ajax call to fail. I had to go back to using real DOM methods to get it done. So, your 3rd to last line should be changed…

As you probably know, the jQuery 1.2 core now supports cross-domain JSONP downloads natively. So there’s probably not much need for the plugin any more. I haven’t tested the new code yet, but it probably does a fine job.