views:

75

answers:

1

I have a stream onto which serialized objects representing messages are dumped periodically. The objects are one of a very limited number of types, and other than the actual sequence of bytes that arrives, I have no way of knowing what type of message it is.

I would like to simply try to deserialize it as an object of a particular type, and if an exception is thrown, try again with the next type.

I have an interface that looks like this:

public interface IMessageHandler<T> where T : class, IMessage { 
  T Handle(string message);
}

// elsewhere:

// (These are all xsd.exe-generated classes from an XML schema.)
public class AppleMessage : IMessage { ... }
public class BananaMessage : IMessage { ... }
public class CoconutMessage : IMessage { ... }

Then I wrote a GenericHandler<T> that looks like this:

public class GenericHandler<T> : IMessageHandler<T> where T: class, IMessage {
  public class MessageHandler : IMessageHandler {
    T IMessageHandler.Handle(string message) {
      T result = default(T);

      try {
        // This utility method tries to deserialize the object with an 
        // XmlSerializer as if it were an object of type T.
        result = Utils.SerializationHelper.Deserialize<T>(message);
      } catch (InvalidCastException e) {
        result = default(T);
      }

      return result;
    }
  }
}

Using my GenericHandler<T> (or something similar to it), I'd now like to populate a collection with handlers that each handle a different IMessage (the Apple*, Banana*, and Coconut* classes in my example). Then I want to invoke each handler's Handle method on a particular message to see if it can be deserialized. If I get a null result, move onto the next handler; otherwise, the message has been deserialized.

That looks something like this:

I wound up doing something like this:

public object Process(string message) {
  var handlers = new ArrayList {
    new MessageHandler<AppleMessage>(),
    new MessageHandler<BananaMessage>(),
    new MessageHandler<CoconutMessage>(),
  }

  foreach (IMessageHandler<IMessage> h in handlers) {
    var result = h.Handle(message);

    // Message successfully handled! Return the result.
    if (result != null) { return result; }
  }

  // Couldn't handle the message; return null.
  return null;
}

I'm pretty sure this won't work because of covariance restrictions, however, since an IMessageHandler<AppleMessage> is not an IMessageHandler<IMessage>, even though an AppleMessage is an IMessage. Is there a different way to do this?

Additionally, is there a better way to deserialize data of unknown (but restricted) type?

+1  A: 

Extend your message with a field that indicates the type, so you can choose the right deserializer.

Optionally, use an existing protocol such as protobuf so you don't have to reinvent the wheel.

dtb
+1 for not reinventing. This problem has been tackled before, for sure.
spender