Technorati Tags:
jQuery,
ASP.NET,
JSON Recently I was working on a small project where we wanted to make requests to go get some data and pass it back via XML or JSON to a jQuery control. So I picked out a control that I thought would work (as it had hooks there for init, load, and other callback slots) and then thought ok, how the hell am I going do this. Having never really created my own or used any HttpHandler (.ashx) before, I found out that this would be the way to do this.
The Goal: Make XML or JSON AJAX requests to the server, pick up some data, send it back and parse with jQuery. Something pretty common but this was my first time attempting it with jQuery and first time using JSON. And the reason do to this was for deferred loading of the jQuery plug-in that I was going to use. To make a long story short, here’s a bit of information on how I did this.
In one of my load functions on the jQuery plug-in, I ended up deciding to make JSON requests instead of XML for primarily 2 reasons:
1) Obviously JSON is less data over the wire as compared to XML. Just compare or look below to see that JSON has no tags like XML does, making it less overall. It’s also much easier and less to read than XML. To me it’s like the analogy of reading tableless design in an HTML page vs. clunky horrible table based layout design with a crap load of uneccessary tags.. <table><tr><td>, blah blah..who wants that crap in mark-up. Well same for JSON vs. XML, who wants all those XML tags when you’ve got JSON! Anyway, to me it’s just cleaner and I like JSON now a lot.
2) jQuery JSON parses it much easier than parsing XML. While it’s still very easy to parse XML with jQuery, it’s even easier with JSON because you have less code to deal with and also better yet, you get a nice typed object once you receive the data back that you can work with to access the variables in your returned JSON
So here’s how it looks using the jQuery getJSON method:
1: $.getJSON("http://someurl/SomeHandler.ashx?action=getproducts&ids=" + ids,
2: function(data)
3: {
4: ..some other code
5:
6: someplugin.add(i, decode(data[d].Image));
7:
8: ...rest of code
9: }
10: );
Decode is just decodes HTML characters if any:
1: function decode(s) {
2: return s.replace(/&/g, "&")
3: .replace(/"/g, '"')
4: .replace(/'/g, "'")
5: .replace(/</g, "<")
6: .replace(/>/g, ">");
7: };
Notice data[d].Image. Data is returned back to the function after successfully parsed by the getJSON method. This parsing happens automatically. And only if successful enters into the function via whatever parameter name you give it. It doesn’t matter what you name the parameter in function(). Now that we have the data, you can access it via index and then simply access the variables using a period. So Image was a name in my JSON name/value pair:
[
{
"Image": "\u003cp\u003e\u003cimg src=\http://www.xxx.com/image/25.jpg\ alt=\"SomeText\"\u003e\u003c/p\u003e\u003cp\u003e\u003ca href=\"somepage.aspx\"\some text\u003c/a\u003e\u003c/p\u003e\u003cp\u003e$20.95\u003c/p\u003e",
"ProductID": "523"
},
{
"Image": "\u003cp\u003e\u003cimg src=\http://www.xxx.com/image/44.jpg\ alt=\"SomeText\"\u003e\u003c/p\u003e\u003cp\u003e\u003ca href=\"somepage.aspx\"\some text\u003c/a\u003e\u003c/p\u003e\u003cp\u003e$20.95\u003c/p\u003e",
"ProductID": "524"
},
]
by the way, make sure you validate the retuned JSON using a JSON text validator.
Now on my .ashx side, I simply used the built-in JavaScriptSerializer in .NET 3.5.
In my .ashx, I simply created a function that cycles through products but populates another generic list based off a custom class that only has properties for what I really want to pass. I don’t want to serialize the entire Product object because that’s too much data so I created a small class called ImageList to hold the values I’m actually going to really want to send back as JSON:
1: public class MyCustomHandler : IHttpHandler {
2:
3:
4: public bool IsReusable {
5: get {
6: return false;
7: }
8: }
9:
10: public void ProcessRequest(HttpContext context)
11: {
12: ...some code and then eventually:
13:
14: GetProductsJSON(context, products);
15: }
16:
17: public class ImageList
18: {
19: public string Image { get; set; }
20: public string ProductID { get; set; }
21: }
22:
23: private void GetProductsJSON(HttpContext context, string[] products)
24: {
25: context.Response.ContentType = "application/json";
26: context.Response.Charset = "utf-8";
27:
28: List<ImageList> imageList = new List<ImageList>();
29:
30: foreach (Product p in products)
31: {
32: StringBuilder productInfo = new StringBuilder();
33:
34: productInfo.AppendFormat(string.Format(@"<p><img src=""{0}"" alt=""{1}""></p>", ImageUrl(p.Image, false), p.AltTag));
35: productInfo.AppendFormat(@"<p><a href=""{0}"">{1}</a></p>", ProductURL(p), p.Name);
36: productInfo.AppendFormat("<p>{0}</p>", ProductUtil.ProductPrice(p));
37:
38: imageList.Add(new ImageList { ImageTag = productInfo.ToString(), ProductID = p.Id.ToString()});
39: }
40:
41: string jsonString = imageList.ToJSON();
42: context.Response.Write(jsonString);
43: }
44:
45: }
A few pointers (common mistakes or just good info here):
1) Be sure that you set the content type and charset correctly. And some settings don’t work for IE but will in FireFox. You obviously want your returned JSON to be parsed successfully in both IE, FireFox and the rest so it’s very important that you use the following:
context.Response.ContentType = "application/json";
context.Response.Charset = "utf-8";
or just
context.Response.ContentType = "application/json;charset=utf-8";
either or will do the trick. Don’t make the mistake of only specifying the ContentType as "application/json" and then forgetting to set a Charset!
2) If you are not using .NET 3.5, you can still get at the JavaScriptSerializer class by including the AJAX 1.0 library from Microsoft
3) Make sure the returned JSON does not have an extra comma at the end of the JSON string…jQuery will not parse as it’s not valid JSON
4) getJSON function of jQuery eats any errors on the returned JSON so you won’t get any notification that hey, the parse failed. So if you really want to trap errors, I would recommend using the $.ajax method instead. Here’s how you’d do the equivilant above with $.ajax and trapping any errors on failure of the JSON parsing:
1: $.ajax({
2: type: "GET",
3: url: "http://localhost:59396/somesite/SomeHandler.ashx?action=getproducts&ids=" + ids,
4: //data: "ids=" + ids,
5: dataType: "json",
6: success: function(data) {
7: alert(data.length);
8:
9: ...some code
10: carousel.add(i, decode(data[d].Image));
11: ...rest of code
12: }
13: },
14:
15: error: function() {
16: alert("An error has occurred. Please try again.");
17: }
18: });