Working with untyped JSON in a WCF service

Since NetFX 3.5, WCF has support for working with JSON data. You can define your data contracts according to the JSON “schema” you expect to receive / want to return, plug them into your operation contracts, and your server is talking JSON. With this feature you can define the contract such as:

  1. public class Person
  2. {
  3.     public string Name { get; set; }
  4.     public int Age { get; set; }
  5.     public Person[] Children { get; set; }
  6. }
  7. [ServiceContract]
  8. public interface ITest
  9. {
  10.     [WebInvoke(RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
  11.     int Create(Person person);
  12.     [WebGet(ResponseFormat = WebMessageFormat.Json)]
  13.     Person Find(int personId);
  14. }

and handle requests or return responses such as the ones below:

  1. { "Name": "John Doe",
  2. "Age": 30,
  3. "Children": [
  4. { "Name": "John Jr", "Age": 8 },
  5. { "Name": "Jane Doe", "Age": 5}]
  6. }

So this feature works quite well, with only one problem. You need to know the “schema” of the data you’re receiving (or returning). There are some cases where the service doesn’t know it upfront – for example, when the definition of the schema is part of the message itself (and you want to populate a data grid from it):

  1. {
  2.     "columns": ["Name", "Age", "Occupation"],
  3.     "data": [
  4.         { "Name": "John Doe", "Age": 30, "Occupation": "Accountant" },
  5.         { "Name": "Jane Roe", "Age": 29, "Occupation": "Doctor" }
  6.     ]
  7. }

Currently, unless you want to parse the JSON yourself (or let WCF parse the JSON into “XML” using the JSON/XML mapping, and parse the information from the “XML” nodes yourself), WCF doesn’t support this scenario well. You’re tied to the data contract, and if you don’t have a data contract, you’re stuck.

In comes JsonValue

In Silverlight (since SL2), there is an API which deals with JSON in an untyped way. The JsonValue classes provide a DOM in which one can load a JSON document, and work with it without linking it to any specific CLR types. I like to think of JsonValue as the equivalent to XElement in the XML world: JsonValue : DataContractJsonSerializer :: XElement : DataContractSerializer. Silverlight, however, is a client-only platform, and as such the JsonValue classes didn’t have any integration with the service model (i.e., it can’t be used in an operation contract).

We found that the JsonValue abstraction, however, is quite interesting, so we decided to port it to the desktop framework, and hook it up to WCF. So you can now consume that “schema-less” value

  1. [ServiceContract]
  2. public class withUntypedJson
  3. {
  4.     [WebInvoke(ResponseFormat = WebMessageFormat.Json, UriTemplate = "/SumFields/{fieldName}")]
  5.     int SumFields(string fieldName, JsonObject input)
  6.     {
  7.         int result = 0;
  8.         if (input.ContainsKey("data"))
  9.         {
  10.             JsonArray data = input["data"] as JsonArray;
  11.             if (data != null)
  12.             {
  13.                 for (int i = 0; i < data.Count; i++)
  14.                 {
  15.                     if (data[i].ContainsKey(fieldName))
  16.                     {
  17.                         result += data[i][fieldName].ReadAs<int>();
  18.                     }
  19.                 }
  20.             }
  21.         }
  22.  
  23.         return result;
  24.     }
  25. }

Now, besides porting the code from Silverlight, we also added additional features such as Linq support, better casting, among others, to make the experience of using the new APIs simpler. The code above, for example, can be rewritten simply as

  1. [ServiceContract]
  2. public class withUntypedJson
  3. {
  4.     [WebInvoke(ResponseFormat = WebMessageFormat.Json, UriTemplate = "/SumFields/{fieldName}")]
  5.     int SumFields2(string fieldName, JsonObject input)
  6.     {
  7.         var values = from d in input.ValueOrDefault("data")
  8.                      select d.Value.ValueOrDefault(fieldName).ReadAs<int>(0);
  9.         return values.Sum();
  10.     }
  11. }

 

I want to use it; now what?

Since the next iteration of .NET Framework (where this feature may be included) is still a long time away, we decided to publish the code to enable users to take advantage of this feature now. As of this morning, we have a new Codeplex site at https://wcf.codeplex.com which contains the binaries needed to use this feature and all its source code, so if you need it the way it is, simply use it. If you need some change, you can either open an issue and wait for the next update (which should be fairly quick), or even fix it yourself, so you don’t get blocked. The JsonValue code is on the “jQuery Support” link on the main page.

Go ahead, download it, let us know what you think!

https://wcf.codeplex.com

More information

I only touched the surface of this new API. You can find more information at the following pages: