Using jQuery with ASP.NET Web Services and JSON

kick it on DotNetKicks.com

By now, hopefully everyone's played with jQuery for client-side JavaScript work and knows it eliminates the fuss of manually writing Ajax code. In this post, I'll briefly cover the idiosyncrasies of using jQuery Ajax to consume .NET 3.5 web services that serve JSON. Although I mostly show examples of ASPX pages with WebMethods, all the JavaScript herein applies to JSON-enabled ASMX and WCF services as well. In the next post, I'll introduce a simple script that makes all this even easier, burying these semantics so you can forget everything you read in this one.

If you'd just like to learn by seeing a working example of jQuery and service methods (in ASPX pages) without, look at the end of this post.

Sending a Request

If you've used jQuery to work with ASP.NET web services using JSON, you've probably learned the boilerplate code below. If you haven't, this is how to send a JSON request (to the page msg.aspx serving the method GetLast(), in this illustration):

$.ajax({
	type: "POST",
	url: "msg.aspx/GetLast",
	data: "{}",
	contentType: "application/json; charset=utf-8",
	dataType: "json",
	success: getLastSuccess,
	error: getLastError
});

Let's get this out of the way first: type is always "POST".

Now, if the request is bad (e.g. a GET, non-existent service method) and is to a method defined in the class of an ASPX page, $.ajax() will fire the error function, passing the XHR object as the first argument to error. The responseText of the XHR will contain the HTML of the ASPX page. This is because ASP.NET will serve the page when the request to the service method is rejected, and responseText is the body of the HTTP response.

// A BAD REQUEST WITH "GET"
$.ajax({
	type: "GET",
	url: "msg.aspx/GetLast",
	data: "{}",
	contentType: "application/json; charset=utf-8",
	dataType: "json",
	success: getLastSuccess,
	error: function(xhr) {
		// alert()s the HTML of msg.aspx 
		alert(xhr.responseText);
	}
});

If the request is fine, but an exception goes unhandled in the service, the error function will fire as well, and the responseText will be the exception serialized to JSON. It will only include the Message, StackTrace, and ExceptionType members, however.

Sending Arguments to Service Methods

In the $.ajax() options used to send a request, data is a JSON string of an object containing the ASP.NET method arguments. ASP.NET services always expects an object. If you're not sending any arguments, set data: "{}" (as shown above). If you try null or leave it undefined, it's a bad request.

To send arguments to a service, create an object whose member names match the argument names of the service method. For example, say we've got this method:

[WebMethod]
public static void PostMessage
	(DateTime timestamp, string author, string message)
{
	...
}

Construct an object to send to the service method parameters as shown below. This object will be serialized to JSON and sent as the data in $.ajax(). Member names must match, but order needn't.

var message = {
	timestamp: new Date(),
	author: "Chris",
	message: "Tag! You're it."
};

Once we have an object whose members match the ASP.NET method's arguments, it needs to be serialized into a JSON string. Be safe, use a script that does it for you, like this one. Call that script's JSON.stringifiy(object) to serialize the message object:

$.ajax({
	type: "POST",
	url: "msg.aspx/PostMessage",
	data: JSON.stringify(message),
	contentType: "application/json; charset=utf-8",
	dataType: "json",
	success: function() {
		alert("Message posted");
	}
});

Receiving Data Back

If the request is responded to successfully, the success function in the $.ajax() options is called. If the service method has a return value, success receives a JavaScript object representing the result.

The object will have one and only one member, named "d" (probably short of "data"). Its value is the return value of the service method. For example, the method below is serialized to {"d":"foobar"}:

[WebMethod]
public static string GetLast()
{
	return "foobar";
}

jQuery creates an object out of this JSON after receiving it. The string "foobar" is retrieved by accessing d of the object:

$.ajax({
	type: "POST",
	url: "msg.aspx/GetLast",
	data: "{}",
	contentType: "application/json; charset=utf-8",
	dataType: "json",
	success: function(data) {
		var result = data.d;
		// result == "foobar"
	}
});

What happens if the type returned by a service method is a complex type (e.g. a class)? Let's take a look. Here's a class and service method that returns it:

public class Person
{
	public int Age;
	public string Name;
	public bool Male;
}
 
public partial class _Default : Page 
{
	[WebMethod]
	public static Person GetChris()
	{
		return new Person() { Age = 120, Name = "Chris", Male = true };
		// serializes to:
		// {"d":{"__type":"Person","Age":120,"Name":"Chris","Male":true}}
	}
}

When the GetChris() is result is received, it's a JavaScript object whose member names match the field names of the Person class, with an additional member named "__type" (two underscores), where __type is equal to "Person":

$.ajax({
	type: "POST",
	url: "Default.aspx/GetChris",
	data: "{}",
	contentType: "application/json; charset=utf-8",
	dataType: "json",
	success: function(data) {
		var result = data.d;	
 
		var type = result.__type; // == "Person" (String)
		var name = result.Name;   // == "Chris" (String)
		var age  = result.Age;    // == 120 (Number)
		var male = result.Male;   // == true (Boolean)
 
		debugger;
	}
});

Dealing with Dates

Primitives and classes are simple to deal with. Their conversions are out-of-the-box. There's one that's a little trickier: .NET DateTime objects. When a service serializes a DateTime object, it comes back as a specially-formatted string. Consider this method:

[WebMethod]
public static DateTime WhatTimeIsIt()
{
	return DateTime.Now.ToUniversalTime();
}

.NET serializes this response to {"d":"/Date(1238800185359)/"}. This string is not converted to a JavaScript Date object when returned by jQuery:

$.ajax({
	...
	url: "time.asmx/WhatTimeIsIt",
	success: function(data) {
		var time = data.d;
		// time == "/Date(1238800185359)/"
	}
});

The numbers in the JSON string are a unix time, but in ms instead of seconds. This is the amount of time that has elasped between Jan. 1, 1970 at midnight UTC and the date the DateTime object represents. If the number is negative, it is before the 1970 epoch.

Parsing it in JavaScript and creating a Date object out of it isn't difficult to accomplish using Regular Expressions though:

function jsonParseDate(obj) {
	if (typeof obj !== "string") {
		return obj;
	}
 
	var match = obj.match(/^\/Date\((-?\d+)\)\/$/);
 
	if (!match) {
		return obj;
	}
	return new Date(parseInt(match[1]));
};
 
$.ajax({
	type: "POST",
	url: "time.asmx/WhatTimeIsIt",
	data: "{}",
	contentType: "application/json; charset=utf-8",
	dataType: "json",
	success: function(data) {
		// data.d is a string = "/Date(1238800185359)/"
		var time = jsonParseDate(data.d);
		// time is now a Date object
		// alert()s "Friday, April 03, 2009 4:09:45 PM"
		alert(time.toLocaleString());
	}
});

If data.d is not a JavaScript Date object, it is left unchanged. This works fine if the WebMethod returns only a DateTime, but what if it returns a complex type that has one or more DateTime member? No problem. Just add a little recursion:

function dateFix(obj) {
 
	if (obj === null) {
		return obj;
	}
 
	// ASP.NET JSON date?
 
	if (typeof obj === "string") {
		var match = obj.match(/^\/Date\((-?\d+)\)\/$/);
 
		// nope, regular string
		if (!match) {
			return obj;
		}
 
		// yup, JSON-serialized DateTime
		return new Date(parseInt(match[1]));
	}
 
	// string or number
 
	if (typeof obj !== "object") {
		return obj;
	}
 
	// array or object
 
	jQuery.each(obj, function(key, val) {
		obj[key] = dateFix(val);
	});
 
	return obj;
}
 
$.ajax({
	type: "POST",
	url: "blog.asmx/GetLastPost",
	data: "{}",
	contentType: "application/json; charset=utf-8",
	dataType: "json",
	success: function(data) {
		data = dateFix(data);
		// any strings for JSON dates are now JavaScript Date objects
	}
});

dateFix(obj) converts any JSON date strings in obj (and all objects within it) into actual JavaScript Date objects.

Example Code, Next Post

You can download a working example of this code here. There's not a whole lot to it, but if you're like me, there's enough quirks and repetition to do something about it. In the next post, I introduce a simple script that takes away the redundancy of specifying that $.ajax() options object, unwrapping returned objects for d, and fixes JSON dates before returning the data to success.

kick it on DotNetKicks.com

Tags: , , , , , , , ,

13 Responses to “Using jQuery with ASP.NET Web Services and JSON”

  1. Simplify jQuery with ASP.NET Web Services and JSON Says:
    April 8th, 2009 at 2:03 am

    [...] my last post, I gave a quick introduction to using jQuery for JSON communication with ASP.NET web and WCF [...]

  2. Chris Says:
    May 19th, 2009 at 8:33 am

    You would not believe how many sites I had to plow through before I find an actual explanation on the breakdown of the syntax! Thank you so much!

  3. Chris Says:
    May 29th, 2009 at 2:43 am

    @Chris,

    Glad I could help! If you have any suggestions that could help me streamline the entry so that people get to the meat sooner, let me know! Take care, Chris.

  4. Paul Shriner Says:
    June 27th, 2009 at 12:14 am

    Great tutorial, it is the exact architecture we just selected.

    You save some proof of concepting time.

  5. lokyrte Says:
    October 12th, 2009 at 6:01 pm

    Ваш проект полезен для людей в теме. Давно отслеживаю за вашим развитием проекта с каждым днем всё лучше. Спасибо за топик. Обязательно у себя в блоге ссылку про пост оставлю. Инфа реально качественная

  6. Chris Says:
    November 12th, 2009 at 11:05 pm

    Glad I could help!

  7. Meghavani Prashant Says:
    November 15th, 2009 at 2:47 am

    Good Article but still one thing is missing how I can Pass Object From Json and accept object from webMethod. You have provided example of passing object but i have to recieve each value as sepereate not as object in webMethod

    For example

    Public Shared Function RegisterMemberObject(ByVal FirstName As String, ByVal LastName As String, ByVal Email As String) As Register

    End Function

    Not

    Public Shared Function RegisterMembertest(ByVal objRegister As Register) As Register

    End Function

  8. nawaz Says:
    December 20th, 2009 at 11:58 pm

    how we call a webservice using json?

  9. kapil lokhande Says:
    January 13th, 2010 at 12:27 am

    Hi great article………………thanks very much.

    If i want to obtain the return values from web method. how to obtain from response.d please reply

  10. mohanrao Says:
    February 16th, 2010 at 4:40 am

    Nice article.. Its Very useful for me..

    Thanks

  11. David Says:
    February 16th, 2010 at 8:25 am

    I see how you passed a date to a page. Can you show how you pass an XMLDOM to a aspnet web service? I can do it using the old MS behaviors (HTC), but I cannot get this done with JQuery. I’m sure it is possible. Thanks.

  12. Mayur Says:
    March 3rd, 2010 at 12:35 am

    As per above example, if my JSON response of type List then how i can handle this response in jquery.

  13. Mayur Says:
    March 3rd, 2010 at 2:56 am

    Thanks! I got the solution.

Leave a Reply

To include code, use <pre lang="csharp">// code...</pre>, where "csharp" is any language (e.g. "javascript", "html", "xml", etc).