Windows Phone 7 OData CTP, nope use REST and JSON

First things first the OData CTP for Windows Phone 7 is exactly that – a CTP - therefore expectations are not high but I did have them set at medium.  Due to the issues I was hitting i ended up having to use REST to consume my WCF data service then write a bunch of code to deserialize the JSON response. So this post will take you through how to do this.

But first, what issues did I encounter – well after being able to successfully to generate the service reference using dataservice util I was able to consume the service successfully and i felt yay big ticks this is awesome.  I then went to implement serverside paging, cool serverside paging out of the box thats pretty neat.  However when I went to then consume the next page from the service the Continuation Token was always null.  This ment that I was unable to navigate to the next page set with the link that should have been returned.  Note:  - if anyone managed to get this working with the CTP please drop me a link and/or some sample code.

Checking out Nicks blog – Windows Phone 7 Data: Json WCF Data Service with IIS 7 Compression I figured out how to construct a REST request using fiddler i tried by application/atom+xml and application/json – both results had the next link :( … With this in mind i really had to narrow down if it was the CTP or not – I created a .NET 4.0 win forms app and generated the service reference  and used the same code to consumed it successfully with the Continuation token being set.  At this point i settled that it is something was from the CTP….

After spending a lot of time trying to get to the bottom of it – using reflector to figure out where the error was coming from because the OData CTP source was not available i finaly decided to cut my losses and just consume the feed using REST.  Funnyly enough, but not so funny at the time, it took less time to write the data contract to deserialize the JSON then I spent fighting the CTP.  To do this I did the following:

Consume your service using REST to see the format of the returned data:

1. Construct request in this case to Posts and add the header accept: application/json then press execute

REST request to WCF Data service

REST request to WCF Data service

2. Take the response and format it using an online formatter to make life easy on yourself

{
   “d”:{
      “results”:[
         {
            "__metadata":{
               "uri":"http://localhost/TestServices/SomeService.svc/Posts(guid'ccb89791-a49b-4224-924d-0e0bc56d8ffb')",
               "type":"Model.Post"
            },
            "Id":"ccb89791-a49b-4224-924d-0e0bc56d8ffb",
            "Title":"some title",
            "Content":"Some long content"
         }
      ],
      “__next”:”http://localhost/TestServices/SomeService.svc/Posts?$skiptoken=guid’ccb89791-a49b-4224-924d-0e0bc56d8ffb’”
   }
}

3. Create your DataContract so that it can consume a response of the format above.  Note the nasty bit about this is the nesting of the above.  I created a generic representation of this so that this can be re-used for your other tables.  So this is what we are building:

Datacontract for JSON deserialization of WCF Data Service

Datacontract for JSON deserialization of WCF Data Service

and the code is as follows: Note the name of the data member maps to the sample JSON for the serialization / deserialization of the JSON content:

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace JSON.DataContract
{
    [DataContract] //Gets the outer {}
    public class BaseJSONResult<T> where T : BaseMyContent
    {
        [DataMember(Name = "d")] //Gets the d
        public ComposedResult<T> Result { get; set; }
    }

    [DataContract]
    public partial class ComposedResult<T> where T : BaseMyContent
    {
        [DataMember(Name = "results")]
        public List<T> MyContents { get; set; }

        [DataMember(Name = "__next")]
        public string NextLinkUri { get; set; }
    }

    [DataContract(Name = "__metadata")]
    public class MetaData
    {
        [DataMember(Name = "uri")]
        public string Uri { get; set; }

        [DataMember(Name = "type")]
        public string Type { get; set; }
    }

    [DataContract]
    public abstract class BaseMyContent
    {
        [DataMember(Name = "__metadata")]
        public MetaData MetaData { get; set; }
    }

    public partial class Post : BaseMyContent
    {
        [DataMember]
        public Guid Id { get; set; }
        [DataMember]
        public string Title { get; set; }
        [DataMember]
        public string Content { get; set; }
    }

    [DataContract]
    public partial class SomeOtherClass : BaseMyContent
    {
        [DataMember]
        public Guid Id { get; set; }
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Caption { get; set; }
    }
}

Note: if you have more types you would like to extend the support to just derrive them from BaseMyContent like SomeOtherClass above.
Note: this example does not currently take care of Links between types… but you should easily be able to add support for this.

4. Usage: Use a web request and response and Json serializer to process the results

   HttpWebRequest request = HttpWebRequest.Create("http://localhost/TestServices/SomeService.svc/Posts") as HttpWebRequest;
   request.Accept = "application/json"; //atom+xml";
   request.BeginGetResponse(RequestCallback, request);

The callback where deserialization occurs, post will contain the deserialize content

private void OnRequestCallback(IAsyncResult result)
{
     var request = result.AsyncState as HttpWebRequest;
     var response = request.EndGetResponse(result);
     if (response != null)
     {
          var jsonSerializer = new DataContractJsonSerializer(typeof(BaseJSONResult<Post>)); //Note: DataContractJsonSerializer requires a reference to System.Servicemodel.Web

          BaseJSONResult<Post> post = null;
          using (var stream = response.GetResponseStream())
          {
               post = jsonSerializer.ReadObject(stream) as BaseJSONResult<Post>;
          }

          this.Dispatcher.BeginInvoke(() =>
             PostRetrieved(Post));
     }
}

Now finally you can get at a populated Next link with post.NextLinkUri *wipes teary eyes*

5. If you are having troubles and the deserialization is not working the best tip i can give you is to try serializing your datacontract and cross check it with the data returned from your service. To serialize it do the following:

                
var test = new BaseJSONResult<Post>()
{
    // Make sure you set all the properties
};
using (MemoryStream ms = new MemoryStream())
{
    jsonSerializer.WriteObject(ms, test);
    byte[] bytes = ms.ToArray();
    string json = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
    Console.WriteLine(json); //Compare this with your fiddler result
}

I really can’t wait until the CTP comes out full form with the bugs fixed, it will save a lot of time, but for now I am keeping a wide berth with the intention of returning once it is out of CTP.