.NET 3.5 now includes a JSON serializer in DataContractJsonSerializer. This useful little component makes it pretty painless to create JSON from objects and back. Here's some code to serialize and deserialize JSON to a string and back:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
Customer person = new Customer();
person.Name = "John Jones";
person.Entered = new DateTime(2007, 10, 10);
person.Addresses.Add(new Address());
person.Addresses.Add( new Address() );
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Customer));
MemoryStream ms = new MemoryStream();
ser.WriteObject(ms, person);
string json = Encoding.Default.GetString(ms.ToArray());
ms.Close();
Response.Write("<pre>" + Server.HtmlEncode(json) + "</pre>");
// *** Start from scratch with deserialization
//ms = new FileStream(Server.MapPath("jsonoutput.txt"), FileMode.Open);
ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
ser = new DataContractJsonSerializer(typeof(Customer));
Customer person2 = ser.ReadObject(ms) as Customer;
ms.Close();
Response.Write(person2.Name);
}
[Serializable]
public class Customer
{
public string Name = "John";
public DateTime Entered = DateTime.Now;
public List<Address> Addresses = new List<Address>();
}
[Serializable]
public class Address
{
public string Street = "32 Kaiea Place";
public string City = "Paia";
public string State = "HI";
public string Zip = "96779";
}
}
Like most other serializers in .NET, to serialize you provide the Type to serialize and a Stream to serialize into. Here a memory stream is used to read the content into a string. To go back the reverse is used - load the string into a memory stream then read the stream and deserialize back into an object.
The output generated is plain old JSON (ie. not MS AJAX style hopped up JSON <g>) (marked up a bit here to make it more readable):
{"Addresses":[{"City":"Paia","State":"HI","Street":"32 Kaiea Place","Zip":"96779"},
{"City":"Paia","State":"HI","Street":"32 Kaiea Place","Zip":"96779"}],
"Entered":"\/Date(1192010400000-1000)\/","Name":"John Jones"}
The good news is that the serializer does fairly well with plain old .NET objects and collections/dictionaries. As you can see above it turned the List of objects into an array that can be consumed on the client and can be turned back into the list on the return trip.
Two way serialization like this makes perfect sense but you should also be able to utilize this functionality in retrieving generic JSON data from the client and feed it to the server to deserialize. This can be pretty handy especially if you are NOT using MS AJAX or even any other framework or simply need to return JSON as an alternate output mechanism (say in an MVC Framework request).
Deserialization works based on a .NET signature which the incoming JSON data has to match. This means that as long as the inbound JSON object matches the type signature of the type you want to create, the deserialization will work. What's nice is that the JSON signature doesn't have to match exactly. So if GetPerson() is expecting a Person object and the client only sends the Name and Entered the deserialization still works.
I'm glad this works because this is a common scenario in AJAX client applications where you often just send data that you actually changed rather than everything back to the server and since it's so damn easy in JavaScript to create objects on the fly it's easy to parcel up data that you want to update in a small object map.
The other reason this is important is because unlike a standalone Web application a WCF service that's using WebHttpBinding doesn't have access to the actual POST buffer so you can't just pick up regular POST variables for example. Even though there's WebOperationContext that's accessible as part of a WCF WebHttp service this object doesn't have access to the POST entity body, so the only effective way to pass data to the server with such a service is via the parameter payload. Using JSON on the client and sending it to the server becomes the most effective way to do this.
Couple other things of note: DataSets will serialize, but they serialize as an XML string not as an array of rows of objects, which would have been preferable. As it is using a DataSet client side in AJAX like this isn't going to be terribly useful unless you load the contents into a DOM and parse it that way. Not bloody likely <s>...
Fortunately in .NET 3.5 it's also fairly easy to take a DataSet and turn it into a List of typed objects with something like this:
List<Customer> custResult =
(from c in ds.Tables["CustomerList"].AsEnumerable()
where c.Field<string>("Company").CompareTo("B") > 0
select new Customer {
Name = c.Field<string>("Company"),
Entered = c.Field<DateTime?> ("Entered")
} ).ToList();
which then CAN be serialized. But of course the object returned must have the Customer type defined in some way first for this to work.
Note also that Anonymous types cannot be serialized because they are not marked as Serializable, so the following does NOT work:
var TPerson = new { Name = "Rick", Company = "West Wind", Entered = DateTime.Now };
DataContractJsonSerializer ser1 = new DataContractJsonSerializer( TPerson.GetType() );
MemoryStream ms1 = new MemoryStream();
ser1.WriteObject(ms1,TPerson);
string json1 = Encoding.Default.GetString(ms1.ToArray());
Response.Write(json1);
ms1.Close();
Shame that because this would could be quite useful in some scenarios for data shaping and returning a final response back to a client that is a stripped down and focused type rather than say a full customer object. I wish there was some way to force anonymous types to be created with a Serializable flag.
Note also that the DataContractSerializer does not generate JSON that can be directly consumed by MS AJAX. MS AJAX serialized JSON objects look a little bit different:
{"d":{"__type":"ScriptCallbacks.BusinessObjects.CustomerEntity",
"PhoneNumbers":{
"HomePhone":"(503) 121-1212",
"WorkPhone":"(503) 333-1222",
"Entered":"\/Date(1198908717056)\/"},
"Orders":null,
"Customerid":"ANTON",
"Companyname":"Antonio Moreno Taqueria",
"Contactname":"Antonio Moreno",
"Contacttitle":"Owner"
}
}
Note that MS AJAX generates a top level object and then injects a __type property into the actual object's data. Funky... and one of the main reason I really don't like working with MS AJAX. It's just not compatible with anything but itself and makes direct client syntax quite funky.
I don't see a way to make the DataContractJsonSerializer creat MS AJAX style automatically, but if you use the WCF Serialization mechanics (WebHttpBinding) it has options to generate MS AJAX style JSON via the enableWebScript or using WebScriptServiceFactory configuration. Unfortunately there are conflicts between various service attributes (UriTemplate can't be used with script services for example) that make it difficult to reuse a single service both ways. <shrug> One more reason how the proprietary MS Ajax JSON format complicates matters. I'll have more on the various WCF service models that allow serving JSON in a later post.
I was toying with the idea of using this serializer to replace the one I've built for my libraries (in wwHoverPanel), but given the fact that it won't deal with DataSets/DataRow in a meaningful way, and that you can't serialize anything that isn't explicitly typed puts a crimp in my existing code. I have some code that returns anonymous types returned from LINQ to be passed to the AJAX client and I'm not inclined to give that up. It's extremely useful in a number of business object result scenarios.
But overall it's very cool that this functionality has been baked into the .NET framework like this. The DataContractJsonSerializer is easy to use and it's now easy to create JSON service type behavior from just about anywhere. I think the MVC Framework in particular can benefit from this providing a relatively easy mechanism for creating JSON views for example.
Other Posts you might also like