Tags: , , , | Categories: JSON, Code Development, MVC, Web Posted by nurih on 1/16/2012 5:38 PM | Comments (0)

In a previous posting, I discussed replacing the stock MVC serializer used for the JsonResult exposed when you use the controller method Json(..) instead of View.

This was all find and dandy. But how – you may wonder – can I call actions that contain complex parameters? Do I need a special binder? Should I write my own?

The answer is mostly "no". As Phil Hack blogged, the ASP.NET MVC framework already contains value providers that take care of that for you. All you need to do is ensure that your Javascript calls your action using some very specific header and mime types.

Here is a function that may help you

   1: <script type="text/javascript">
   2:        var postToAction = function (sender) {
   3:            var json = $('textarea#' + sender.data).val();
   4:            $("textarea#myResponse").val('working...');
   5:             $.ajax({
   6:                 url: document.location.href,
   7:                 type: 'POST',
   8:                 dataType: 'json',
   9:                 data: json,
  10:                 contentType: 'application/json; charset=utf-8',
  11:                 success: function (data) {
  12:                     var replyText = JSON.stringify(data);
  13:                     $("textarea#myResponse").val(replyText);
  14:                 }
  15:             });
  16:         };
  17:     </script>

The special sauce here is the dataType and contentType together with the POST method. The rest is pretty much how jQuery wants to be called.

On line 6 you will note that I'm POSTing to the same URL as the browser is on – you may want to fiddle with that.

We  call this function by hooking up a button, something like:

   1: $("button#myButtonId").click('myTextAreaWithJsonContent', postToAction);

In your own implementation you will no doubt compose some object in JavaScript, or create a Json object. In the code above, I'm simply using Json formatted string: Line 3 gets the value from a text area element. If you compose a complex object and want to send it, you will need to convert it into a Json string. jQuery 1.5.1 – which I happen to use – already contains this facility, JSON.stringify(x) would do the trick. You can see it here used  on line 12, for the inbound response which I simply stringify and hydrate a response text area. But this is only demo code – you will wire up the success (and failure function- right?) to your own logic.

But back to the core issue: Yes, MVC supports submitting Json and hydrating my controller action, but how do I persuade it to use my custom chosen super duper serializer rather than the stock one?

The answer is: Create a custom ValueProviderFactory and instantiate your favorite serializer in there. A bit of inspection on the MVC source code on CodePlex.com reveals the stock implementation.

Here's a modified version, which isolates the serialization in a clear way:

   1: public class MyJsonValueProviderFactory : ValueProviderFactory
   2: {
   3:     public override IValueProvider GetValueProvider(ControllerContext controllerContext)
   4:     {
   5:         if (controllerContext == null)
   6:         {
   7:             throw new ArgumentNullException("controllerContext");
   8:         }
  10:         if (!controllerContext.HttpContext.Request.ContentType.StartsWith(
  11:                 "application/json", StringComparison.OrdinalIgnoreCase))
  12:         {
  13:             return null;
  14:         }
  17:         object value = Deserialize(controllerContext.RequestContext.HttpContext.Request.InputStream);
  19:         if (value == null)
  20:         {
  21:             return null;
  22:         }
  24:         var bag = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
  26:         PopulateBag(bag, string.Empty, value);
  28:         return new DictionaryValueProvider<object>(bag, CultureInfo.CurrentCulture);
  29:     }
  31:     private static object Deserialize(Stream stream)
  32:     {
  33:         string str = new StreamReader(stream).ReadToEnd();
  35:         if (string.IsNullOrEmpty(str))
  36:         {
  37:             return null;
  38:         }
  40:         var serializer = new JavaScriptSerializer(new MySpecialTypeResolver());
  42:         return serializer.DeserializeObject(str);
  43:     }
  45:     private static void PopulateBag(Dictionary<string, object> bag, string prefix, object source)
  46:     {
  47:         var dictionary = source as IDictionary<string, object>;
  48:         if (dictionary != null)
  49:         {
  50:             foreach (var entry in dictionary)
  51:             {
  52:                 PopulateBag(bag, CreatePropertyPrefix(prefix, entry.Key), entry.Value);
  53:             }
  54:         }
  55:         else
  56:         {
  57:             var list = source as IList;
  58:             if (list != null)
  59:             {
  60:                 for (int i = 0; i < list.Count; i++)
  61:                 {
  62:                     PopulateBag(bag, CreateArrayItemPrefix(prefix, i), list[i]);
  63:                 }
  64:             }
  65:             else
  66:             {
  67:                 bag[prefix] = source;
  68:             }
  69:         }
  70:     }
  72:     private static string CreatePropertyPrefix(string prefix, string propertyName)
  73:     {
  74:         if (!string.IsNullOrEmpty(prefix))
  75:         {
  76:             return (prefix + "." + propertyName);
  77:         }
  78:         return propertyName;
  79:     }
  81:     private static string CreateArrayItemPrefix(string prefix, int index)
  82:     {
  83:         return (prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]");
  84:     }
  85: }

It all really boils down to the same ceremony as the default implementation, except on line 40 and 42 we now get to use our own special serializer. Woot!

To use this instead of the built in one, you will modify your global.asax.cs Application_Start() to include something like

   1: var existing = ValueProviderFactories.Factories.FirstOrDefault(f => f is JsonValueProviderFactory);
   2: if (existing != null)
   3: {
   4:     ValueProviderFactories.Factories.Remove(existing);
   5: }
   6: ValueProviderFactories.Factories.Add(new MyJsonValueProviderFactory());

where the built in one gets removed and my custom one gets added. Pretty straightforward.

With this technique and the one described in my previous post, you are ready to use the full power of MVC as an API supporting a nice strongly typed parameter for the back end developer and supporting fully customizable JSON in and out of methods. No real need for other frameworks or technologies for the serving jQuery needs.

Depending on your methods, you may even get away with one set of actions serving both form posts and jQuery invocation.

Happy coding!

Tags: , , , | Categories: Code Development, MVC, Web, JSON, Serialization Posted by nurih on 1/11/2012 6:10 PM | Comments (0)

For various reasons you may find that the default JsonResult returned by invoking the controller method such as

return Json([data);

Is unsuitable for the consumer. The main issue most often encountered is that this method uses the JsonResult which in turn uses the JavaScriptSerializer with no access to the JavaScriptTypeResolver.

This means that you can provide that serializer a parameter specifying you own custom type resolver.

Other issues, such as maximum recursion depth and maximum length and type resolvers can be simply configured in web.config. See  Configuring JSON Serialization section on MSDN.

Back to the core problem though.

To override the JsonResult, we would need to do 2 things:

1) Create our own custom JsonResult implementation

2) Tell the controller to use ours rather than the stock one.

A new JsonResult is needed because the base one hard codes the construction of the JavaScriptSerializer.

So here we go. Some CTRL+C, CTRL+V later from the open source MVC on Codeplex gives us

   1: public class TypedJsonResult : JsonResult
   2: {
   3:     public override void ExecuteResult(ControllerContext context)
   4:     {
   5:         if (context == null)
   6:         {
   7:             throw new ArgumentNullException("context");
   8:         }
  10:         if ((JsonRequestBehavior == JsonRequestBehavior.DenyGet)
  11:             && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
  12:         {
  13:             throw new InvalidOperationException("JsonRequest GetNotAllowed");
  14:         }
  16:         var response = context.HttpContext.Response;
  18:         response.ContentType = !string.IsNullOrEmpty(ContentType) ? ContentType : "application/json";
  20:         if (ContentEncoding != null)
  21:         {
  22:             response.ContentEncoding = ContentEncoding;
  23:         }
  25:         if (Data != null)
  26:         {
  27:             var serializer = new JavaScriptSerializer(new BiasedTypeResolver());
  28:             response.Write(serializer.Serialize(Data));
  29:         }
  30:     }
  31: }

You will note on line 27 that we're still using the JavaScriptSerializer, but this time we're controlling its construction and decided to give it our own type resolver. More on that type resolver in a bit.

Next, we want to our controller(s) an easy way to choose our TypedJsonResult rather than the stock one. Luckily, the controller boils down the call Json(data) and several other signatures to a call to a virtual signature which we may simply override, like so:

   1: protected override  JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)
   2: {
   3:     return new TypedJsonResult { Data = data, ContentType = contentType, ContentEncoding = contentEncoding, JsonRequestBehavior = behavior };
   4: }

That's it! On line 3 you will notice we return our custom TypedJsonResult, whereas the stock implementation would have returned the JsonResult.

If this new behavior is desired everywhere, then you would probably want to place this override in a base controller of your own, and have all your controllers inherit it.

Other powers this bestows on you is of course using some other serializer altogether, such as Json.NET or whatever you fancy.

Now back to my own type resolver. You could, after all, use the SimpleTypeResolver built into the framework, which works quite well. However, it introduces fairly long type names – frowned upon by my clients consuming this Json on other platforms, and also doesn't enable me to map my own type names to a type of my choice. Enter the BiasedTypeResolver.

   1: private static readonly Dictionary<string, Type> _KnownTypes;
   3: static BiasedTypeResolver()
   4: {
   5:     _KnownTypes = new Dictionary<string, Type> ();
   6:     var appDomain = AppDomain.CurrentDomain;
   7:     foreach (var assembly in appDomain.GetAssemblies().Where(a => a.GetName().Name.EndsWith(".ValueObjects")))
   8:     {
   9:         foreach (var type in assembly.GetTypes().Where(t => !t.IsInterface && !t.IsAbstract))
  10:         {
  11:             _KnownTypes[type.Name] = type;
  12:         }
  13:     }
  14: }
  16: public override Type ResolveType(string id)
  17: {
  18:     var result = Type.GetType(id);
  19:     if (result != null || _KnownTypes.TryGetValue(id, out result))
  20:     {
  21:         return result;
  22:     }
  23:     throw new ArgumentException("Unable to resolve [" + id + "]", "id");
  24: }
  26: public override string ResolveTypeId(Type type)
  27: {
  28:     if (type == null)
  29:     {
  30:         throw new ArgumentNullException("type");
  31:     }
  33:     return type.Name;
  34: }

This resolver spelunks specific assemblies only (those named [whatever].ValueObjects which are my naming convention for POCO public objects) and catalogs them into a dictionary by short type name.

It doesn't know how to resolve 2 types of the same name if they only differ by namespace, but then again I'd ask "how come you would define 2 classes of the same exact name in your solution?" You can define type names to whatever suites your needs.

The resolver's responsibility is twofold: Given a string , return the System.Type that corresponds to it. Given a type, return a name for it. The former is used during deserialization, the latter when serializing.

Now you may not need or like this particular type resolver implementation. But now that you can inject your own, possibilities are limitless. These can range to configuration based type resolution, versioning decisions base on some revision upgrades etc.

Note also that upon deserialization, the type resolver is only called when a type discriminator exists in the JSON stream. That is, a complex type that doesn't contain "__type":"foo" will be serialized by the JavaScriptSerializer by matching the target member name rather than the resolver. This is nice because the JSON can contain strategically placed type discriminators for polymorphic reasons on some members, but be left terse and bare otherwise.

Hopefully, this helps you, or me-of-the-future when the gray cells got filled with the next great thing..

Happy Coding!