A few days ago somebody asked me why I bothered in the West Wind Ajax Toolkit with building a custom JSON parser, which is a valid question. After all Microsoft now has two separate JSON parsers in the .NET framework so why use a custom parser?
Some time ago - before the days of Microsoft Ajax 1.0 - I built my own .NET JSON serializer and deserializer, because there's was limited support for JSON serialization and more importantly deserialization available at the time. There were a few frameworks out there - including my favorite which was Anthem at the time - which included JSON serializers that could send JSON data to JavaScript clients for consumption. Generating JSON output is actually pretty straight forward as the encoding mechanisms are easy to produce even for complex and hierarchical objects from managed code. The other end - de-serialization into a specific .NET type - is considerably more difficult to do because the language format for JSON is fairly terse with many redundant symbols. Parsing JSON is no picknic especially if you want to build a parser that can validate JSON up front or during processing as invalid (and in fact, the one I built doesn't).
When I first started out on this path I used Anthem's serializer as a base line and then tacked on my own de-serializer. Right around the time that happened Microsoft released the fist builds of ASP.NET AJAX (ATLAS at the time) which also included a new JavaScriptSerializer, which I was glad to see at the time. I figured my code wouldn't be needed for too long so I didn't spend much time on making the de-serializer work perfect in all scenarios and let it be 'good enough' for now to wait for release in the .NET framework. Heck, after all deserialization of client data coming back to the server is less common than sending data, especially when talking about complex types coming back from the client. But the need quickly accelerated as we all discovered the power of building JSON style RPC service interfaces that could effectively feed data back into the front Web UI. It turned out to be a long haul to MS AJAX 1.0 and in the meantime I needed a solution and so the parser evolved.
Fast forward to today. Currently the .NET framework contains two JavaScript JSON parsers to choose from:
- JavaScriptSerializer in System.Web.Extensions
- DataContractJsonSerializer in System.Runtime.Serialization.Web
JavaScriptSerializer in System.Web.Extensions
This component is part of the ASP.NET AJAX update library in System.Web.Extensions and allows serialization and deserialization of data. The interface is pretty straight forward and works reasonably well for most data types in application scenarios. The serializer works by simply passing an object and returning a string JSON result. Type support is adequate although there are a some useful omissions.
In short you can do stuff like this:
StockServer ss = new StockServer();
StockQuote quote = ss.GetStockQuote("MSFT");
JavaScriptSerializer ser = new JavaScriptSerializer();
string json = ser.Serialize(quote);
StockQuote quote2 = ser.Deserialize<StockQuote>(json);
to serialize and then deserialize content.
At first glance this looks great - it's nice and easy but there's a small, but annoying shortcoming here: The Deserialize method is generic and so requires a type known at compile time - it can't be type variable, but has to be a type constant.
StockQuote quote2 = ser.Deserialize<StockQuote>(json);
The generic parameter must be a type constant (or typeof()) in order to work, but you can't do something like this:
Type stockType = quote.GetType();
...
StockQuote quote2 = ser.Deserialize<stockType>(json); // doesn't work
because generics are compiler based and create effectively overloaded method stubs for each type variation. Bummer in this case. I really hate it when APIs provide ONLY generic interfaces and no way to specify a type dynamically.
Anyway, this means that out of the gate you can't easily create dynamic routines that do things like parsing a number of parameters say to forward calls to an object dynamically as you might do to build a JSON service.
Actually - after posting a small blip on Twitter today - I got a hint from Stuart Thompson about using Reflection to provide a generic parameter at runtime. With that it's possible to access to the Deserialize method with a type variable:
public static object FromJsonString(string json, Type type)
{
// *** Have to use Reflection with a 'dynamic' non constant type instance
JavaScriptSerializer ser = new JavaScriptSerializer();
object result = ser.GetType()
.GetMethod("Deserialize")
.MakeGenericMethod(type)
.Invoke(ser, new object[1] { json });
return result;
}
It's ugly but it works for this scenario.
To make this more reusable you can create an Extension class that provides JSON features ToJson() on object and FromJson() on string along with classic static methods:
/// <summary>
/// Json Extensions based on the JavaScript Serializer in System.web
/// </summary>
public static class JsonExtensionsWeb
{
/// <summary>
/// Serializes a type to Json. Note the type must be marked Serializable
/// or include a DataContract attribute.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToJsonString(object value)
{
JavaScriptSerializer ser = new JavaScriptSerializer();
string json = ser.Serialize(value);
return json;
}
/// <summary>
/// Extension method on object that serializes the value to Json.
/// Note the type must be marked Serializable or include a DataContract attribute.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToJson(this object value)
{
return ToJsonString(value);
}
/// <summary>
/// Deserializes a json string into a specific type.
/// Note that the type specified must be serializable.
/// </summary>
/// <param name="json"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object FromJsonString(string json, Type type)
{
// *** Have to use Reflection with a 'dynamic' non constant type instance
System.Web.Script.Serialization.JavaScriptSerializer ser
= new System.Web.Script.Serialization.JavaScriptSerializer();
object result = ser.GetType()
.GetMethod("Deserialize")
.MakeGenericMethod(type)
.Invoke(ser, new object[1] { json });
return result;
}
/// <summary>
/// Extension method to string that deserializes a json string
/// into a specific type.
/// Note that the type specified must be serializable.
/// </summary>
/// <param name="json"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object FromJson(this string json, Type type)
{
return FromJsonString(json, type);
}
public static TType FromJson<TType>(this string json)
{
System.Web.Script.Serialization.JavaScriptSerializer ser
= new System.Web.Script.Serialization.JavaScriptSerializer();
TType result = ser.Deserialize<TType>(json);
return result;
}
}
One thing that's nice about the JavaScriptSerializer is that it works on most common .NET types and doesn't require any special object attributes. I think this is an important feature of any JSON serializer because unlike typical 'Service' interfaces often times JSON is used purely for UI services to feed data and having to specify any sort of attribute or markup contract only gets to be counter productive.
This is especially useful when used in combination with dynamic type results through anonymous types explicitly created in code or - probably more to the point - LINQ based custom results that are filtered down to only return the exact data required to the client.
Using the JavaScriptSerializer with the above extension classes allow you do the following with an anonymous type:
// *** Can serialize anonymous types with JavaScriptSerializer
var valueDyn = new
{
Name = "Rick Strahl",
Entered = DateTime.Now.AddDays(-10),
Number = 100.22M
};
json = valueDyn.ToJson();
this.WriteValue(json);
Note that while you can Serialize an anonymous type, you cannot Deserialize it because anonymous types don't have a parameterless constructor. However, if you have a concrete type that matches the object signature of the object serialized into JSON you can still potentially import the data. So you can serialize just the data you need on the client, but you can deserialize back into the full originating type.
Another issue: JavaScriptSerializer chokes on circular references which are common in data models (like LINQ to SQL).
Anyway - this dynamic behavior is quite useful and is a big plus of this serializer over the DataContractSerializer as we'll see in a minute.
The JavaScriptSerializer has another problem though: It's now marked obsolete and according to the documentation and compiler error we're supposed to use DataContractJsonSerializer for Json serialization. I think it was a good idea that JSON moved into a more generic portion of the .NET framework, but unfortunately the DataContractJsonSerializer has even more restrictive interface than JavaScriptSerializer.
So in summary:
Pros:
- Easy to use
- Doesn't require any marker interfaces to serialize objects
Cons:
- API requires static types for Deserialization (generic method only)
- Dynamic Types require Reflection and extra code
- Class has been deprecated by Microsoft
- Doesn't work with a few common data structures
- Chokes on circular references
DataContractJsonSerializer
DataContractJsonSerializer is part of .NET 3.5 WCF extensions and lives in System.ServiceModel.Web. The primary purpose of this new Serializer - and it shows - is to handle the JSON serialization support for the .NET 3.5 WCF Http extensions which provide REST and AJAX services that are specifically geared to the Web model. As such the WCF 3.5 REST and AJAX services are quite at odds with the rest of WCF in that they don't interoperate really as smoothly as other service types that can usually just tweak a few configuration settings and enjoy the same behavior for various different protocols. The difference here is that the HTTP protocols are highly specialized to REST and AJAX scenarios which doesn't fit with the SOAP style messaging normally used in WCF. IOW, the HTTP REST/AJAX services are the odd man out when it comes to WCF functionality.
The DataContractSerializer clearly is geared towards serving the needs of the WCF platform as it uses and even requires use of attributes to adorn objects and members to allow them to be serialized and deserialized. And therein is probably the biggest downfall of the DataContractJsonSerializer: It requires WCF compatible types and attributes.
The model for serialization is also a bit more verbose than the JavaScriptSerializer, although it's nothing that a small wrapper method can't fix easily. Here's how to serialize and deserialize:
StockServer ss = new StockServer();
StockQuote quote = ss.GetStockQuote("IBM");
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(StockQuote));
MemoryStream ms = new MemoryStream();
ser.WriteObject(ms, quote);
string json = Encoding.Default.GetString(ms.ToArray());
// *** Deserialize
ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
ser = new DataContractJsonSerializer(typeof(StockQuote));
StockQuote quote2 = ser.ReadObject(ms) as StockQuote;
ms.Close();
The DataContractJsonSerializer is a little more work to use than the JavaScriptSerializer as it works off input streams, but it's still pretty simple. The code used can be easily abstracted and the serializer works well as long as you're working off serializable types.
For completeness here's the same extension class using the DataContractSerializer as the base converter:
public static class JsonExtensions
{
/// <summary>
/// Serializes a type to Json. Note the type must be marked Serializable
/// or include a DataContract attribute.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToJsonString(object value)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(value.GetType());
MemoryStream ms = new MemoryStream();
ser.WriteObject(ms, value);
string json = Encoding.Default.GetString(ms.ToArray());
return json;
}
/// <summary>
/// Extension method on object that serializes the value to Json.
/// Note the type must be marked Serializable or include a DataContract attribute.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToJson(this object value)
{
return ToJsonString(value);
}
/// <summary>
/// Deserializes a json string into a specific type.
/// Note that the type specified must be serializable.
/// </summary>
/// <param name="json"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object FromJsonString(string json, Type type)
{
MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json));
DataContractJsonSerializer ser = new DataContractJsonSerializer(type);
object value = ser.ReadObject(ms);
ms.Close();
return value;
}
/// <summary>
/// Extension method to string that deserializes a json string
/// into a specific type.
/// Note that the type specified must be serializable.
/// </summary>
/// <param name="json"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object FromJson(this string json, Type type)
{
return FromJsonString(json,type);
}
}
The big problem with DataContractSerializer is that it only works off serializable types. You need to either have the type marked with a [DataContract] and explicit [DataMember] values or the type must be marked as [Serializable]. Both might make good sense for WCF style service scenarios but for JSON messages sent from Web applications this requirement is pretty much a deal killer. This also means no anonymous type support so if you want to use this serializer with data returned from LINQ queries you'll have to make sure you return full entities and these entities are marked up with the appropriate attributes. Yechh...
Pros:
- Microsoft's official JSON parser
- Works with dynamic types
- Updated and integrated with WCF
- Deals with circular references (also some control over depth of serialization)
Cons:
- Requires marked up types - Serializable or DatContract/DataMember required
- Requires a number of assembly refs
So where does that leave us?
As I mentioned at the outset, the two parsers are workable, but both have their issues. Clearly Microsoft is pushing towards DataContractJsonSerializer, but with it come some limitations that make it somewhat unusable in certain scenarios. For me the inability to push results from dynamic LINQ queries to the client is proving to be fatal.
JavaScriptSerializer works well enough with a few exceptions, but the problem here is the the Obsoleting by Microsoft. Also I have a fair bit of legacy code that uses DataSets and DataTables fed back from list based data of business objects and it's very useful to feed that to the client as object arrays. This will change eventually, but in the meantime this old code needs to continue to work with this data. This can be done with JavaScriptSerializer by creating special TypeResolvers but it's a pain - this should just be built in even if some functionality is one way.
The other issue is that both serializers are using some proprietary formats for things like dates. It's not a bad solution as long as you're running MS AJAX or a compatible framework (my toolkit also supports those formats), but if a client passes data in a different format (say new Date() or the new 'suggested' ISO date literal) the deserializers choke, which is problematic when dealing with non-.NET based client JSON serialization.
For me - it means I can't switch to the built in JSON parsers, so for the time being I'm sticking with my existing home grown parser which addresses all of these issues. But, maintenance of a parser is always a pain especially as standard changes and additions are likely to occur. JavaScript serialization is pretty trivial, but the deserialization can get pretty gnarly quickly especially if validation is required :-{. I'd much rather use something out of the box. So I'm hoping that Microsoft will step up and provide something that's a little more light handed than the DataContractJsonSerializer.
There are also several other libraries out there including JSON.Net, Ajax.Net Pro, Anthem.Net and several others that also provide JSON services with varying degrees of flexibility as is the West Wind Ajax Toolkit where you can find the above mentioned free standing JSON parser.
I use JSON serialization daily and for the moment at least I'm glad that I have a custom parser in place because I have more control over the process and can allow any kind of content to go through the parser and format it as needed easily. In the end that's what counts...
Other Posts you might also like