Tuesday, July 28, 2009

Serializing Objects to Json in ASP.NET MVC

I've been working like crazy lately, and haven't been doing much blogging at all (reading or writing). I have a slew of blogs that I want to catch up on. I've barely checked my twitter lately, but reading my blog roll has completely fallen to the way side. The good news is that I've been learning a lot of jQuery, ASP.NET MVC, Linq, and overall enterprise application architecture in a very short period of time! This means that I have a brain full of geekiness just dying to get out.

Building this type of application is really a totally new type of programming for me and has really been putting my brain (and whiteboard) through the some long nights. Over the last few weeks I've really been able to see it all come to light. I'm shooting at a moving target (so to say) and there are changes that pop up along the way that change the system at it's core, where I'm forced to change how objects are constructed and so forth. While I could go on and on about the application I'm working on and all of the problems I've been faced with recently, the topic for this post is strictly related to how easy it is to serialize server side objects to the client.

Problem
I needed a way to represent server side memory objects as client side javascript objects. I have a set of div tags that need to be represented on a canvas (another div) and the user needs to be able to interact with them. Most importantly, I didn't want to re-invent the wheel entirely in javascript by creating all new classes and so forth.

Solution
Json.

Example
Let's say I have an object called "Person" that has a few properties and a collection of PhoneNumbers.

Person
  • Name
  • Address
  • City
  • State
  • Zip
  • PhoneNumbers[]
PhoneNumber
  • Type
  • Number
Now to the meat! In order to serialize these objects to Json, simply create a nested class (ie. a class that is defined within the parent class) that inherits from JavaScriptConverter:

class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public IList PhoneNumber { get; }

public class JsonSerializer : JavaScriptConverter
{
// override SupportedTypes, Serialize(), & Deserialize()
}
}

class PhoneNumber
{
public string Type { get; set; }
public string Number { get; set; }

public class JsonSerializer : JavaScriptConverter
{
// override SupportedTypes, Serialize(), & Deserialize()
}
}

Inside the 'SupportedType' member, simply return a new enumeration of the parent type. For example the SupportedTypes member inside Person.JsonSerializer would look like this:

get { return new[] { typeof( Person ) }; }

Inside the 'Serialize' method, simply cast the obj parameter to your type, and then begin setting each property like so:

var result = new Dictionary();
var person = obj as Person;
if (person == null)
return result;

result["Name"] = person.Name;
result["Age"] = person.Age;
result["Address"] = person.Address;
result["City"] = person.City;
result["State"] = person. State;
result["Zip"] = person.Zip;
result["PhoneNumbers"] = person.PhoneNumbers;

return result;

Inside 'Deserialize' do the same thing, only backwards (not covered here)... In order to take advantage of the Json data, let's initialize a PersonViewModel class. This class will be used by our View pages. Here is a quick example of what this class might look like:

class PersonViewModel
{
public PersonViewModel(Person person)
{
Person = person;
Json = string.Empty;
}

public Person Person { get; private set; }
public string Json { get; set; }

}

This class will be initialized by an Action method on our Controller class. Inside that method is where I initialize the Json property. In order to serialize our data to Json, we need to register our custom JavaScriptConverters with an instance of the JavaScriptSerializer. Let's do that now!

User calls:
http://mywebsite/Person/Edit/123

This will call the 'Edit' action on the PersonController; this will be where all of the things glue together.

public ActionResult Edit(int personID)
{
var person = _repository.GetPerson(personID);

var converters = new List();
converters.Add(new Person.JsonSerializer());
converters.Add(new PhoneNumber.JsonSerializer());

var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(converters);

var viewModel = new PersonViewModel(person);
viewModel.Json = serializer.Serialize(person);

return View(viewModel);

}

Inside the Edit view, we can now simply use a server tag to initialize a variable to be used by the page (in javascript notation). Here is the best part!!

var person = <%= Model.Json %>;

$(document).ready(function(){
alert(person.Name + ' is ' + person.Age + ' years old');
});

Cool huh?

3 comments:

Scott said...

Hey Luc,

That's an interesting approach. The way I did it in a recent ASP.NET MVC project I worked on was to add a toJson extension method to Object.

using System.Web.Script.Serialization;

namespace MyProject.Mvc.Html
{
public static class JsonHelperExtensions
{
static JavaScriptSerializer _serializer = new JavaScriptSerializer();
public static string ToJson(this object o)
{
return _serializer.Serialize(o);
}
}
}

This allowed me to do something similar to what you do in your view

var foo = <%=Model.Person.toJson()%>

Anonymous said...

Or you could do something like this

How to serialize you model to either Json, Xml or Html

You only need to add an attribute to an Action.

:)

Anonymous said...

I agree with Scott. It is an interesting approach but using standard serialization is less code and doesn't polute your data classes with serialization logic.