So, You Want to Speak JSON, eh?

I am actively developing on ASP.NET MVC 3 and that has proven very useful to me as I make the switch to developing for Windows Phone 7.

What?  you say That doesn’t make any sense!

To which I reply: embrace the cloud, homeboy. 

Okay, Wait a Minute

I know, I know, I just went from JSON to MVC 3 to Windows Phone 7 to the cloud. But, here’s the thing: they’re all complimentary.

If you’re looking for the code that makes returning JSON pretty much a freebie, skip ahead. Else { BuckleUp(); }

ASP.NET MVC is great for working with JSON – it supports binding JSON to your model classes natively, even when you’re dealing with complex mappings like collections of entities nested in other entities.  It’s also trivial to return JSON encoded data from your model classes and the result types are native to the framework.  It gets even better with LINQ to JSON and the Json.Net serialization library available on NuGet.

JSON is lightweight, so it makes it a great candidate for pushing data around in mobile scenarios.  If you want to work with anything other than Windows Phone 7 (what? why would you do that?!), then the freebies in the .NET world don’t play so nicely and you’ll want to jump on the JSON bandwagon.  Json.Net works on WP7 as well, so now you’re covered for your soon-to-be-awesome Windows Phone app.  (By the way…go join the movement and get a free Xbox, WP7, $500 and more here, if you’re Canadian…publish some apps!).

Where does the cloud come into this? Thought you’d never ask.  A refresh on the Azure toolkit earlier this year gave us MVC3 deployment templates, so pushing your app out on the web is simple. Find out more http://blogs.msdn.com/b/cdndevs/p/canadadoesazure.aspx.

Speaking JSON

So, you’re doing some MVC 3 dev, you (maybe) are hosting out in the cloud, and you want to knock off some low-hanging fruit to turn your controllers and actions into full-fledged JSON participants?  I was at a conference recently and saw how easy this was in Ruby on Rails, and figured there had to be a way to do this in MVC.  I didn’t want to have to write new versions of all my actions just to respond with the same data as JSON.

Turns out this is too easy, but first we need to understand a little bit about how controllers and actions work.

Incoming requests are handled by the routing framework for us.  A request coming in for Home/Index results in an instance of the HomeController getting created and then our Index action (method) being invoked.  But did you know that you have the chance to use ActionFilters?  If we create our own ActionFilter, we get the chance to modify the parameters coming into the action, we get access to the controller, the HttpContext and more. 

And that’s just on the front end of execution. We get a chance to look at everything again – including the view data – on the back end of execution as well, meaning we can swap out our own result object.  Huzzah! We have a vector!

Using the Vector

Here’s the plan:

  • Let the action execute
  • Check to see if the requesting party was looking for a JSON response, and, if they were,
  • Create a new JsonResult and push the model data from the action into it

 

Seems simple enough, right? Here’s what the implementation looks like (and honestly, there’s as much commenting in there as code):

namespace Json.ActionFilter
{     public class SupportsJson : ActionFilterAttribute     {         // we will use the framework default but allow users to override in the constructor         private JsonRequestBehavior _jsonRequestBehavior = JsonRequestBehavior.DenyGet;         public SupportsJson() {}         // if you want to allow GET (lame!) then be my guest...         public SupportsJson(JsonRequestBehavior behavior)         {             _jsonRequestBehavior = behavior;         }         // this happens AFTER the action is executed and BEFORE the result is returned         // so it's the perfect place to swap out the result object for our JSON one         public override void OnActionExecuted(ActionExecutedContext filterContext)         {             // capture the application types             var applicationTypes = (filterContext.HttpContext.Request.AcceptTypes ?? new string[] {""});             // check to see if json is in there             if (applicationTypes.Contains("application/json"))             {                 // swap out the result if they requested Json                 var model = filterContext.Controller.ViewData.Model;                 filterContext.Result = new JsonResult { Data = model, JsonRequestBehavior = _jsonRequestBehavior };             }         }     }
}

Bam! And just like that we have us some JSON capabilities!

Using the ActionFilter

Here’s an existing ActionResult I have:

        public ActionResult FooCookies(string term)         {             PeopleRepository repository = new PeopleRepository();             var people = repository.GetPeople(term);             return View(people);         }

How much work is it to have it return JSON instead?

        [SupportsJson]         public ActionResult FooCookies(string term)         {             PeopleRepository repository = new PeopleRepository();             var people = repository.GetPeople(term);             return View(people);         }

Tada! That’s right, just add the attribute SupportsJson and instantly you can repurpose any action to return JSON from the controller.

Next Steps

Hey look, you don’t need to be building Windows Phone 7 apps on Azure to take advantage of this.  If you’re building any site using jQuery or jQuery UI, if you work with other mobile platforms or even building for Windows 8, now is a good time to make sure that your data (and complex entity collections) are JSONable. So, you can start by:

Good luck!