One recurring theme that I see in the forums is when a user has a sample JSON document (something that is returned by a service, for example), and he/she wants to deserialize similar JSON documents. That’s the case for most AJAX services which return JSON out there – since there’s no standardized metadata description for JSON, what the sites usually do is to provide a sample response (or request), and the user should infer the schema from that document if he/she wants to work with the data in CLR types (to take advantage of intellisense, static checking, etc). This is similar to the “Paste Xml as Serializable Type” feature which was shipped on the WCF REST Starter Kit, where one would take a sample XML document and the tool would create some types which could be used by the XmlSerializer to consume it.

Using the JsonValue API, I wrote a simple tool which can create a data contract type which can be used with the DataContractJsonSerializer to convert the JSON documents into strongly-typed CLR objects. After loading the JSON into a JsonValue DOM object, the code goes through the tree and creates a System.CodeDom representation of a [ServiceContract] interface with a method which can be used to generate or consume the data. The code will also create CodeDom representations of data contract types which can be used to deserialize the data without needing to go through WCF (i.e., using the DataContractJsonSerializer directly).

For example, given this sample JSON (from the geonames database):

  1. {
  2.     "geonames": [
  3.     {
  4.         "fcodeName": "capital of a political entity",
  5.         "countrycode": "MX",
  6.         "fcl": "P",
  7.         "fclName": "city, village,...",
  8.         "name": "Mexico City",
  9.         "wikipedia": "de.wikipedia.org\/wiki\/Mexiko-Stadt",
  10.         "lng": -99.12766456604,
  11.         "fcode": "PPLC",
  12.         "geonameId": 3530597,
  13.         "lat": 19.428472427036,
  14.         "population": 12294193
  15.     },
  16.     {
  17.         "fcodeName": "capital of a political entity",
  18.         "countrycode": "PH",
  19.         "fcl": "P",
  20.         "fclName": "city, village,...",
  21.         "name": "Manila",
  22.         "wikipedia": "en.wikipedia.org\/wiki\/Manila",
  23.         "lng": 120.9822,
  24.         "fcode": "PPLC",
  25.         "geonameId": 1701668,
  26.         "lat": 14.6042,
  27.         "population": 10444527
  28.     },
  29.     {
  30.         "fcodeName": "capital of a political entity",
  31.         "countrycode": "BD",
  32.         "fcl": "P",
  33.         "fclName": "city, village,...",
  34.         "name": "Dhaka",
  35.         "wikipedia": "de.wikipedia.org\/wiki\/Dhaka",
  36.         "lng": 90.40743827819824,
  37.         "fcode": "PPLC",
  38.         "geonameId": 1185241,
  39.         "lat": 23.710395616597037,
  40.         "population": 10356500
  41.     },
  42.     {
  43.         "fcodeName": "capital of a political entity",
  44.         "countrycode": "KR",
  45.         "fcl": "P",
  46.         "fclName": "city, village,...",
  47.         "name": "Seoul",
  48.         "wikipedia": "de.wikipedia.org\/wiki\/Seoul",
  49.         "lng": 126.97783470153809,
  50.         "fcode": "PPLC",
  51.         "geonameId": 1835848,
  52.         "lat": 37.56825613889526,
  53.         "population": 10349312
  54.     },
  55.     {
  56.         "fcodeName": "capital of a political entity",
  57.         "countrycode": "ID",
  58.         "fcl": "P",
  59.         "fclName": "city, village,...",
  60.         "name": "Jakarta",
  61.         "wikipedia": "de.wikipedia.org\/wiki\/Jakarta",
  62.         "lng": 106.84513092041016,
  63.         "fcode": "PPLC",
  64.         "geonameId": 1642911,
  65.         "lat": -6.214623197035775,
  66.         "population": 8540121
  67.     },
  68.     {
  69.         "fcodeName": "capital of a political entity",
  70.         "countrycode": "JP",
  71.         "fcl": "P",
  72.         "fclName": "city, village,...",
  73.         "name": "Tokyo",
  74.         "wikipedia": "de.wikipedia.org\/wiki\/Tokio",
  75.         "lng": 139.581298828125,
  76.         "fcode": "PPLC",
  77.         "geonameId": 1850147,
  78.         "lat": 35.61488368245436,
  79.         "population": 8336599
  80.     },
  81.     {
  82.         "fcodeName": "capital of a political entity",
  83.         "countrycode": "TW",
  84.         "fcl": "P",
  85.         "fclName": "city, village,...",
  86.         "name": "Taipei",
  87.         "wikipedia": "de.wikipedia.org\/wiki\/Taipeh",
  88.         "lng": 121.531846,
  89.         "fcode": "PPLC",
  90.         "geonameId": 1668341,
  91.         "lat": 25.047763,
  92.         "population": 7871900
  93.     },
  94.     {
  95.         "fcodeName": "capital of a political entity",
  96.         "countrycode": "CN",
  97.         "fcl": "P",
  98.         "fclName": "city, village,...",
  99.         "name": "Beijing",
  100.         "wikipedia": "de.wikipedia.org\/wiki\/Peking",
  101.         "lng": 116.397228240967,
  102.         "fcode": "PPLC",
  103.         "geonameId": 1816670,
  104.         "lat": 39.9074977414405,
  105.         "population": 7480601
  106.     },
  107.     {
  108.         "fcodeName": "capital of a political entity",
  109.         "countrycode": "CO",
  110.         "fcl": "P",
  111.         "fclName": "city, village,...",
  112.         "name": "Bogotá",
  113.         "wikipedia": "de.wikipedia.org\/wiki\/Bogot%C3%A1",
  114.         "lng": -74.08175468444824,
  115.         "fcode": "PPLC",
  116.         "geonameId": 3688689,
  117.         "lat": 4.609705849789108,
  118.         "population": 7102602
  119.     },
  120.     {
  121.         "fcodeName": "capital of a political entity",
  122.         "countrycode": "HK",
  123.         "fcl": "P",
  124.         "fclName": "city, village,...",
  125.         "name": "Hong Kong",
  126.         "wikipedia": "en.wikipedia.org\/wiki\/Hong_Kong",
  127.         "lng": 114.157691001892,
  128.         "fcode": "PPLC",
  129.         "geonameId": 1819729,
  130.         "lat": 22.2855225817732,
  131.         "population": 7012738
  132.     }
  133.   ]
  134. }

The tool will generate the following classes:

  1. //------------------------------------------------------------------------------
  2. // <auto-generated>
  3. //     This code was generated by a tool.
  4. //     Runtime Version:v4.0.30319
  5. //
  6. //     Changes to this file may cause incorrect behavior and will be lost if
  7. //     the code is regenerated.
  8. // </auto-generated>
  9. //------------------------------------------------------------------------------
  10.  
  11. [System.ServiceModel.ServiceContractAttribute()]
  12. public interface IService
  13. {
  14.     [System.ServiceModel.Web.WebInvokeAttribute(BodyStyle = System.ServiceModel.Web.WebMessageBodyStyle.Bare, RequestFormat = System.ServiceModel.Web.WebMessageFormat.Json, ResponseFormat = System.ServiceModel.Web.WebMessageFormat.Json)]
  15.     [System.ServiceModel.OperationContractAttribute()]
  16.     CitiesJsonResponseClass ProcessData();
  17. }
  18.  
  19. [System.Runtime.Serialization.DataContractAttribute()]
  20. public partial class GeonamesClass
  21. {
  22.  
  23.     [System.Runtime.Serialization.DataMemberAttribute()]
  24.     public string fcodeName;
  25.  
  26.     [System.Runtime.Serialization.DataMemberAttribute()]
  27.     public string countrycode;
  28.  
  29.     [System.Runtime.Serialization.DataMemberAttribute()]
  30.     public string fcl;
  31.  
  32.     [System.Runtime.Serialization.DataMemberAttribute()]
  33.     public string fclName;
  34.  
  35.     [System.Runtime.Serialization.DataMemberAttribute()]
  36.     public string name;
  37.  
  38.     [System.Runtime.Serialization.DataMemberAttribute()]
  39.     public string wikipedia;
  40.  
  41.     [System.Runtime.Serialization.DataMemberAttribute()]
  42.     public double lng;
  43.  
  44.     [System.Runtime.Serialization.DataMemberAttribute()]
  45.     public string fcode;
  46.  
  47.     [System.Runtime.Serialization.DataMemberAttribute()]
  48.     public int geonameId;
  49.  
  50.     [System.Runtime.Serialization.DataMemberAttribute()]
  51.     public double lat;
  52.  
  53.     [System.Runtime.Serialization.DataMemberAttribute()]
  54.     public int population;
  55. }
  56.  
  57. [System.Runtime.Serialization.DataContractAttribute()]
  58. public partial class CitiesJsonResponseClass
  59. {
  60.  
  61.     [System.Runtime.Serialization.DataMemberAttribute()]
  62.     public GeonamesClass[] geonames;
  63. }

To deserialize that JSON, one could use the “CitiesJsonResponseClass” created above and pass it directly to the DataContractJsonDeserializer:

  1. DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(CitiesJsonResponseClass));
  2. MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json));
  3. CitiesJsonResponseClass result = (CitiesJsonResponseClass)dcjs.ReadObject(ms);

The tool can be found at http://carlosfigueira.me/JsonUtilities/JsonToContract.htm. I didn’t thoroughly test it, so I’m sure there may be some issues with it. The source code can be found as a sample in the MSDN code gallery. Since the hoster I use for my site only supports partial trust, I had to create a simple CodeDom compiler to go through the CodeDom representation and output the code, but if you’re running the code locally you won’t need to do that – the code has two paths for full and partial trust.

I have a list of some improvements for the tool (serialization only, non-bare operation contracts, WebGet instead of WebInvoke, etc), which I’ll plan on doing in the future. Let me know if you have additional ideas!