Search code examples
c#xmlxml-serialization.net-assemblydatacontract

Using custom DataContractResolver with multiple assemblies


I have following setup in a MEF application:

Assembly MyBaseAssembly:

namespace My.Namespace
{
    [DataContract]
    public class Container
    {
        [DataMember]
        public Data Item { get; set; }
    }

    [DataContract]
    public class Data
    {
        [DataMember]
        public string Foo { get; set; }
    }
}

Assembly SecondAssembly, references the MyBaseAssembly:

namespace My.Another.Namespace
{
    [DataContract]
    public class SecondData : Data
    {
        [DataMember]
        public string Bar { get; set; }
    }
}

Somewhere deep inside of my application I create a Container object:

Container container = new Container();
container.Item = new SecondData { Bar = "test" };

I want to serialize and de-serialize the container object. Since the SecondAssembly is a MEF-module, I need to dynamically detect and resolve the types in the data contract, so the KnownTypeAttribute is not a good solution.

I created a custom DataContractResolver, but I don't know how do I get the assembly information for de-serialization.

On serialization, I get following XML:

<d4p1:SecondData
    xmlns:d6p1="http://schemas.datacontract.org/2004/07/My.Another.Namespace"
    i:type="d7p1:My.Another.Namespace.SecondData">
...
</d4p1:SecondData>

This is the default DataContract serialization behavior: we get the type name and the type namespace, but there is no (explicit) assembly information!

Trying to de-serialize this XML, I cannot determine which assembly to use for resolving the type:

class SerializationTypeResolver : DataContractResolver
{
    ...

    public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
    {
        Type result = knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null);
        if (result == null)
        {
            // Here, I cannot rely on the declaredType parameter,
            // because it contains the declared type which is Data from MyBaseAssembly.
            // But I need the SecondData from the SecondAssembly!

            string assemblyName = ???; // How do I get this assembly name?
            string fullTypeName = typeName + ", " + assemblyName;
            result = Type.GetType(fullTypeName);
        }

        return result;
    }
}

So my question is: what is the good way to store and get assembly name while serializing and de-serializing the DataContracts?


Solution

  • Why not use AssemblyQualifiedName already when serializing? Like this:

    internal class SerializationTypeResolver : DataContractResolver {
        public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace) {
            // not necessary to hardcode some type name of course, you can use some broader condition
            // like if type belongs to another assembly
            if (type.Name == "SecondData") {
                XmlDictionary dictionary = new XmlDictionary();
                // use assembly qualified name
                typeName = dictionary.Add(type.AssemblyQualifiedName);
                typeNamespace = dictionary.Add("http://tempuri.org"); // some namespace, does not really matter in this case
                return true;
            }
            return knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace);
        }
    
        public override Type ResolveName(string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver) {
            if (typeNamespace == "http://tempuri.org") {
                return Type.GetType(typeName); // assembly qualified already
            }
            return knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null);
        }
    }