Welcome to Part 1 of a multi-part series on JavaScript Object Notation (JSON) support in the Microsoft .NET Framework. In this article, we'll focus on WCF's DataContractJsonSerializer. You can find the other parts of this series at these locations:

  • Part 1 - An exploration of the DataContractJsonSerializer class
  • Part 2 - An exploration of the JavaScriptSerializer class
  • Part 3 - JSON serialization from WCF (including a REST primer)
  • Part 4 - Using the JavaScriptConverter class to customize JSON serialization

This is the beginning of a series in which I'll explore support for JavaScript Object Notation (JSON) in the Microsoft .NET Framework. In this part, I'll be using the DataContractJsonSerializer. You can use the DataContractJsonSerializer to stream any serializable POCO into its JSON representation. Of course, this is handy for working with AJAX controls but you aren't limited to using JSON in a web context at all. Look at the following simple class definition in C#:

[Serializable]
public class Thingy
{
 public string Name { get; set; }
 public int ID { get; set; }
 public int Age { get; set; }
}

Classes that will be serialized or deserialized with the DataContractJsonSerializer in .NET must be marked as [Serializable] or as a [DataContract]. Code that creates an instance of this class and serializes it to JSON using the WriteObject method of the DataContractJsonSerializer might look like this:

private static void Main()
{
 // create a Thingy and dehydrate it into JSON
 var ser = new DataContractJsonSerializer( typeof( Thingy ) );
 var t1 = new Thingy
 {
 ID = 1,
 Name = "Kevin",
 Age = 44
 };
 var mstrm = new MemoryStream();
 ser.WriteObject( mstrm, t1 );
 var jsonText = Encoding.UTF8.GetString( mstrm.GetBuffer() );
 Console.WriteLine( jsonText );
}

would yield the following text on the console window:

{
 "k__BackingField":44,
 "k__BackingField":1,
 "k__BackingField":"Kevin"
}

Simple enough. Notice that JSON doesn't qualify any of the data by type. You can see that the names of the automatic properties are odd-looking, too. This is because the C# compiler names the backing fields for automatic properties in a way that they are guaranteed not to clash with the names you can create but it also makes them ugly and hard to read. Let's mark the Thingy class as a DataContract instead to see what effect that has on the output of our little program.

[DataContract]
public class Thingy
{
 [DataMember]
 public string Name { get; set; }
 [DataMember]
 public int ID { get; set; }
 [DataMember]
 public int Age { get; set; }
}

If you run the small program above against the DataContract-oriented Thingy class, you now get this much cleaner-looking output:

{
 "Age":44,
 "ID":1,
 "Name":"Kevin"
}

That's much better, don't you think? It's also smaller which will make tranmission time faster, too. OK, now let's focus on deserialization. Of course, you could deserialize the MemoryStream containing the JSON text into a new Thingy instance using the ReadObject method of the DataContractJsonSerializer. But that's no fun at all. We should do something a bit more interesting with our time. Since JSON doesn't qualify the types of the properties, we should be able to deserialize the JSON text into a different class that has properties with compatible names. But does order matter? And does it matter if the JSON text contains data for properties that don't exist in the target type? Let's test those ideas. Look at the following class definition for an OtherThingy class:

[DataContract]
public class OtherThingy
{
 [DataMember]
 public float Age { get; set; }
 [DataMember]
 public string Name { get; set; }
}

The OtherThingy class has an Age property but it's a floating point type, unlike the integer Age property in the Thingy class. Also notice that the Age property of the OtherThingy class is specified before the Name property reading top to bottom. Finally, note that the OtherThingy class contains no ID property at all. Will the serialized Thingy JSON text deserialize correctly into a new OtherThingy instance? Here's a program that tests these ideas:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
 
// to run this code, be sure to reference
// the following assemblies:
// A. System
// B. System.Core
// C. System.Runtime.Serialization
// D. System.ServiceModel.Web
// E. System.XML
 
[DataContract]
public class Thingy
{
 // we'll dehydrate an instance of this class
 // through the JSON data contract serializer
 [DataMember]
 public string Name { get; set; }
 [DataMember]
 public int ID { get; set; }
 [DataMember]
 public int Age { get; set; }
}
 
[DataContract]
public class OtherThingy
{
 // then we'll rehydrate into an instance of
 // this class - notice the differences:
 // 1. the Age properties types don't match
 // 2. the ID property is missing here
 // but that's OK - it will still work!
 [DataMember]
 public float Age { get; set; }
 [DataMember]
 public string Name { get; set; }
}
 
internal static class Program
{
 private static void Main()
 {
 // create a Thingy and dehydrate it into JSON
 var ser = new DataContractJsonSerializer( typeof( Thingy ) );
 var t1 = new Thingy
 {
 ID = 1,
 Name = "Kevin",
 Age = 44
 };
 var mstrm = new MemoryStream();
 ser.WriteObject( mstrm, t1 );
 
 // dump Thingy object details to console
 Console.WriteLine( "Serializing {0} from:", t1.GetType().Name );
 t1.ObjectProperties().ForEach(
 kvp = Console.WriteLine( "({2}) {0} = {1}",
 kvp.Key, kvp.Value,
 kvp.Value.GetType().Name ) );
 
 // rewind the stream so it can be read
 mstrm.Position = 0;
 
 // rehydrate an OtherThingy from JSON
 ser = new DataContractJsonSerializer( typeof( OtherThingy ) );
 var t2 = ser.ReadObject( mstrm );
 
 // dump OtherThingy object details
 Console.WriteLine( "Deserialized {0} as:", t2.GetType().Name );
 t2.ObjectProperties().ForEach(
 kvp = Console.WriteLine( "({2}) {0} = {1}",
 kvp.Key, kvp.Value,
 kvp.Value.GetType().Name ) );
 
 // wait for user input
 Console.WriteLine( "Done. Press Enter . . ." );
 Console.ReadLine();
 }
 
 // an extension method to stream object properties
 // into KeyValuePairs - wish I had a Tuple type :(
 private static IEnumerableKeyValuePair>string, object>>ObjectProperties( this object obj )
 {
 foreach (var pi in obj.GetType().GetProperties())
 {
 yield return new KeyValuePairstring,object>( pi.Name, pi.GetValue( obj, null ) );
 }
 }
 
 // an extension method for the missing ForEach
 // of IEnumerable
 private static void ForEachT>( this IEnumerableT> source, ActionT> action )
 {
 foreach (T item in source)
 action( item );
 }
}

The output shows:

Serializing Thingy from:
(String) Name = Kevin
(Int32) ID = 1
(Int32) Age = 44
Deserialized OtherThingy as:
(Single) Age = 44
(String) Name = Kevin

Coolness! It behaved as I had hoped. We'll have more fun with JSON serialization in the next installment.