16 March 2008

JSON services revisited: using a Dictionary as a generic parameter

Somebody at Microsoft deserves a Nobel Prize. Well, that is maybe a bit strong, but I ran into the problem that I wanted to create JSON scriptserver that would accept a 'parameter bag' of unknown size and type. I expected problems in the serialization, since Javascript typing is at best a bit lax, but it turns out that when you define a web service with a parameter of type Dictionary<string, object> dict, the JSON serializer automatically derives the object types from the javascript data! It works like this: suppose we define the following method:
[WebMethod]
public string DictMethod(Dictionary<string, object> dict)
{
  StringBuilder b = new StringBuilder("C# reports<BR/>");
  foreach( string s in dict.Keys )
  {
    b.AppendFormat(string.Format("{0}:{1}={2}<BR/>", s, dict[s].GetType(), 
                   dict[s]));
  }
  return b.ToString();
}
We can call this method from javascript like this
<script type="text/javascript">
  function LoadDict()
  {
    dict = new Object();
    dict["test1"] = "Joost";
    dict["test2"] = 21;
    dict["test3"] = true;
    dict["test4"] = 9999999999;
    dict["test5"] = new Date(2008,09,12,13,14,00);
    DictService.DictMethod( dict, ProcessResult );
  }

  function ProcessResult( WebServiceResult )
  {
    document.write( WebServiceResult );
  }
</script>
If you attach the LoadDict javascript function to some client side event (The OnClientClick of a button for example) and launch it, you will see the following output: C# reports test1:System.String=Joost test2:System.Int32=21 test3:System.Boolean=True test4:System.Int64=9999999999 test5:System.DateTime=12-10-2008 11:14:00 So you can see, the javascript string converted into a true .NET string, the value 21 into an Int32 - but the value 9999999999 is too long for an int and is converted into a Int64, a.k.a. long - I really like that one, for it shows true craftmanship of the programmer ;-). The "true" javascript value is converted into a Boolean finally, the javascript Date is converted into a neat DateTime. And presto, all your stuff is converted into real .NET objects. Of course, you should always used typed parameters when possible, but for some generic solutions this may come in very handy. Complete code downloadable here.

3 comments:

Anonymous said...

You can't pass an IDictionary object to a web service as a parameter. How did you do this with out Serializing it first?

Joost van Schaik said...
This comment has been removed by the author.
Joost van Schaik said...

First of all, I am using a Dictionary, not an IDictionary. Apart form that, you have it exactly right. You can't pass an (I)Dictionary to a web service as a parameter. The SOAP Formatter simply simply won't buy that - but the ASP.NET JSON Formatter will! It works exactly as I described. If you try accessing that web service in the 'normal' way, it will not work. Heck, you can't even get a decent WSDL out of it ;-). But as a JSON service, it works. Don't ask me how, thank the anonymous Microsoft programmer