Serializing and Deserializing derived types or interfaces in WCF

Often you will have the case where you can take as input into a WCF operation contract a base type, which could be one of many derived types, or an interface that could be implemented by many

different classes. Similarly you could be returning a base type or an interface. WCF supports those scenarios fairly well, and is able to handle serialization and deserialization for them.

However, you have to do a few extra things to provide WCF the extra information it needs to work correctly. This blog will tell you what they are…

Lets say I have a service defined like this:

namespace MyNamespace
{
    [ServiceContract]
    public interface IMyServiceInterface
    {
            [OperationContract]
            [WebGet(UriTemplate = "MyObjects",
              ResponseFormat = WebMessageFormat.Json)]
            BaseResponse GetMyObjects(uint projectID,
                                       BaseRequest breq);
    }

    // Here BaseRequest and BaseResponse are 2 base classes 
    // and lets say each has 2 derived classes:
    [DataContract]
    public class BaseRequest
    {
        uint reportID;
    }

    [DataContract]
    public class DerivedRequest1 : BaseRequest
    {
        uint componentID;
    } 

    [DataContract]
    public class DerivedRequest2 : BaseRequest
    {
        uint screenID;
    }

    [DataContract]
    public class BaseResponse
    {
    }

    [DataContract]
    public class DerivedResponse1 : BaseResponse
    {
    } 

    [DataContract]
    public class DerivedResponse2 : BaseResponse
    {
    }

    // Lets say the implementation of that 
    // service is like this:
    public class MyService
    {
        public BaseResponse GetMyObjects(uint
                      projectID, BaseRequest breq)
        {
            BaseResponse bres = null;    

            if (breq is DerivedRequest1)
            {
                bres = new DerivedResponse1(projectID);
            }
            else if (breq is DerivedRequest2)
            {
                bres = new DerivedResponse2(projectID);
            }

            return bres;
        }
    }
}

Now the question is how does WCF know to serialize DerivedResponse1 and DerivedResponse2, and deserialize DerivedRequest1 and DerivedRequest1?

Firstly you have to tell it using the KnownType attribute on the base contracts themselves. Something like this:

[DataContract]
[KnownType(typeof(DerivedRequest1))]
[KnownType(typeof(DerivedRequest2))]
public class BaseRequest
{
} 

[DataContract]
[KnownType(typeof(DerivedResponse1))]
[KnownType(typeof(DerivedResponse2))]
public class BaseResponse
{
}

That should be enough to get it to serialize DerivedResponse1 and DerivedResponse2 but for an incoming request, it does not know which derived request to deserialize to.

Lets say this is how we are making the call to the service from Javascript (using Mootools):


var args = new Object();
args.projectID = 2;
args.breq = new Object();
args.breq.reportID = 4;
args.breq.componentID = 3; 

var req = new Request.JSON(
          {
              url: 'myserviceurl',
              headers: { 'Content-Type': 'application/json' }
          }); 

req.addEvent("success", successfn);
req.addEvent("failure", failurefn);
req.send(JSON.encode(args));

This will fail because WCF will not know that the breq object there was actually meant to be DerivedRequest1. It has to be told that. You can do that by adding a special “__type” (thats a double underscore) member to the breq object. Something like this:

var args = new Object();
args.projectID = 2;
args.breq = new Object();

// Specifying the type of the object as it
// can be of multiple types 
args.breq.__type = "DerivedRequest1:#MyNamespace"; 

args.breq.reportID = 4;
args.breq.componentID = 3; 

var req = new Request.JSON(
          {
              url: 'myserviceurl',
              headers: { 'Content-Type': 'application/json' }
          }); 

req.addEvent("success", successfn);
req.addEvent("failure", failurefn);
req.send(JSON.encode(args));

Now this will work correctly with WCF being able to correctly deserialize breq into DerivedRequest1. The same holds true if instead of the base types you had interfaces that were being implemented

by multiple classes. With the extra information provided by using the KnownType attribute and the “__type” member, you can get the behavior you expect.

Now you can use a client side stub generator, especially if you’re not making the request from JavaScript and you wont have to deal with the “__type” variable, as the stub should be able to insert in that for you.

Digg This
Advertisements

About floatingfrisbee

A programmer/blogger from New York City
This entry was posted in JavaScript, Mootools, web services and tagged , , . Bookmark the permalink.

6 Responses to Serializing and Deserializing derived types or interfaces in WCF

  1. Sandeep says:

    Hello

    Instead of using BaseRequest and BaseResponse classes, can we use IBaseRequest and IBaseResponse interfaces ? is this possible.

    please help me as I need to use interfaces only and not base classes.
    thanks

    • Hi Sandeep,

      Yes, you can use interfaces with your WCF services. The syntax will be pretty much the same. On the JavaScript side you will create and object, set its members and then set the “__type” hint. WCF will automatically deserialize that object into the type specified by “__type”, and give you a variable of the interface type. Keep in mind that in both cases (using interfaces or classes), the constructor of your type is not called. Instead the setters for the properties are called, and so you should be writing your objects in that way.

      Good luck!

      • Alexandru says:

        It doesn’t work with interfaces in this manner.. the KnownTypeAttribute cannot be applied to an interface.
        One solution would be to use the ServiceKnownTypeAttribute in the service, supplying as parameter all of the types implementing your interface. Not sure if there’s any other solution..

  2. Amaran says:

    How can we implement the same for Array of Inherited Objects.
    Suppose this is my method
    public BaseResponse GetMyObjects(List breq)

    How should i pass the value from JSON if both
    DerivedRequest1 adn DerivedRequest2 needs to be passed to the array??

    • Hi Amaran,

      My understanding is that you want to pass in an array of request objects, some of which could be DerivedRequest1 and others DerivedRequest2.

      I haven’t tried this in a while but I think it should work:

      Your service method will look like this

      BaseResponse GetMyObjects(uint projectID, IEnumerable<BaseRequest> requestList);

      You will have to create an array in JavaScript, into which you will insert individual request objects (of both derived types).

      var args = new Object();
      args.projectID = 2;
      args.requestList = new Array();

      args.requestList[0].__type = “DerivedRequest1:#MyNamespace”;
      args.requestList[0].reportID = 4;
      args.requestList[0].componentID = 3;

      args.requestList[1].__type = “DerivedRequest2:#MyNamespace”;
      args.requestList[1].reportID = 7;
      args.requestList[1].componentID = 65;

      Try this and see if it works. I have implemented a service that takes a list as an input before but don’t have the code handy to confirm how I did it.

      Jaspreet

  3. Bryan says:

    Thank you, this was exactly what I needed!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s