After posting I also found this SO identical question How to serialize a derived type as base. The unaccepted second answer by marc for me is the easiest way to resolve this issue. That is:
Decorate the derived class with [DataContract(Name="BaseClass")]
Note that this solution means that derived will transport as base for all every case of transport of this object. For me that was not an issue if it is then you need to go the DataContractResolver route.
Some notes on the DataContractResolver route:
1. This enables you to pass the derived as derived on some calls but as base on other - if you need to do that - if not use about Name= approach.
2. I get an exception using the DeserializeAsBaseResolver from the datacontractrsolver article as it stands because the knownTypeResolver returns false. To fix that I ignor the return value of that call and always return true from TryResolveType. That seems to work.
3. I initially thought that because we were serializing as base that I didnt need [DataContract] on the derived class. That was wrong. The object is serialized as the derived object and derserialized as a base object - so you must decorate the derived with [DataContract] but don't mark any fields as [DataMembers] to avoid them being unnecessarily serialize.
4. If you have a command line host and a service host then you need the code to insert the contract resolver in both. I found it useful to put this as a static in my resolver.
5. Note that that the "Get_gateway_data" string in the call to cd.Operations.Find("Get_gateway_data")
is the name of the contract method that returns the object concerned. You will need to do this for each call that you want this behaviour.
Final code for this approach:
public class DeserializeAsBaseResolver : DataContractResolver {
public static void Install(ServiceHost service_host) {
// Setup DataContractResolver for GatewayProcessing to GatewayData resolution:
ContractDescription cd = service_host.Description.Endpoints[0].Contract;
OperationDescription myOperationDescription = cd.Operations.Find("Get_gateway_data");
DataContractSerializerOperationBehavior serializerBehavior = myOperationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (serializerBehavior == null) {
serializerBehavior = new DataContractSerializerOperationBehavior(myOperationDescription);
myOperationDescription.Behaviors.Add(serializerBehavior);
}
serializerBehavior.DataContractResolver = new DeserializeAsBaseResolver();
}
public override bool TryResolveType(Type type, Type declaredType,
DataContractResolver knownTypeResolver,
out XmlDictionaryString typeName,
out XmlDictionaryString typeNamespace) {
bool ret = knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
//return ret; // ret = false which causes an exception.
return true;
}
public override Type ResolveName(string typeName, string typeNamespace,
Type declaredType, DataContractResolver knownTypeResolver) {
return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null) ?? declaredType;
}
Host code (service or command line):
using (ServiceHost service_host = new ServiceHost(typeof(GatewayServer))) {
// Setup DataContractResolver for GatewayProcessing to GatewayData resolution:
DeserializeAsBaseResolver.Install(service_host);
// Open the host and start listening for incoming messages.
try { service_host.Open(); }