Question

I am attempting to save/load a class to an xml file that contains generic types using a DataContractSerializer. I have the save working, but have realized I can't load it because I don't have the list of knownTypes for the deserializer.

Is there a way of serializing/deserializing this class that would allow me to deserialize it without referencing any of the stored types directly?

Here is my SessionVariables class that I am trying to save/load:

[DataContract]
public class SessionVariables
{
    [DataMember]
    private Dictionary<Type, ISessionVariables> _sessionVariables = new Dictionary<Type, ISessionVariables>();
    private object _syncLock = new object();

    public T Get<T>()
        where T : ISessionVariables, new()
    {
        lock (_syncLock)
        {
            ISessionVariables vars = null;

            if (_sessionVariables.TryGetValue(typeof(T), out vars))
                return (T)vars;

            vars = new T();
            _sessionVariables.Add(typeof(T), vars);

            return (T)vars;
        }
    }

    public IList<Type> GetKnownTypes()
    { 
        IList<Type> knownTypes = new List<Type>();

        knownTypes.Add(this.GetType().GetType()); // adds System.RuntimeType

        foreach (Type t in _sessionVariables.Keys)
        {
            if (!knownTypes.Contains(t))
                knownTypes.Add(t);
        }

        return knownTypes;
    }
}

The different modules of the application extend the ISessionVariables interface to create their own set of session variables, like this:

[DataContract]
public class ModuleASessionVariables : ISessionVariables
{
    [DataMember]
    public string ModuleA_Property1{ get; set; }
    [DataMember]
    public string ModuleA_Property2 { get; set; }
}


[DataContract]
public class ModuleBSessionVariables : ISessionVariables
{
    [DataMember]
    public string ModuleB_Property1{ get; set; }
    [DataMember]
    public string ModuleB_Property2 { get; set; }
}

And a singleton instance of the SessionVariables class is used to access session variables, like this:

singletonSessionVariables.Get<ModuleASessionVariables>().ModuleA_Property1
singletonSessionVariables.Get<ModuleBSessionVariables>().ModuleB_Property2

I got the save working like this:

using (FileStream writer = new FileStream(@"C:\test.txt", FileMode.Create))
{
    DataContractSerializer dcs = new DataContractSerializer(typeof(SessionVariables), singletonSessionVariables.GetKnownTypes());
    dcs.WriteObject(writer, singletonSessionVariables);
    writer.Close();
}

However this method does not work to deserialize the class because I don't know it's known types.

Can I serialize and deserialize generic types when I don't have direct library references to any of the types used? And if so, how?

Was it helpful?

Solution

The problem here is that you aren't just wanting to serialize data, but you also want to serialize data about your data, i.e., (cue the dramatic chipmunk) metadata.

That metadata, in this case, are the types of the models that held the data originally. Normally, this isn't an issue, but as you've discovered if you're taking advantage of polymorphism in your design, your single collection may contain two or more different types, each of which needs to be deserialized to their original type.

This is usually accomplished by saving this Type metadata to the serialized result. Different serialization methods does this in different ways. Xaml serialization uses xml namespaces associated with .net namespaces, then names the elements after the original type name. Json.net accomplishes this via a specific named value saved to the json object.

The default DataContractSerializer is not Type aware. Therefore you need to replace it with a version that understands the .NET Type system and can serialize/deserialize Type metadata to the resulting xml. Luckily, one already exists in the framework, the NetDataContractSerializer.

And that's how you pad a link-only answer. The Aristocrats.

OTHER TIPS

You could accomplish this using a custom DataContractResolver. This allows you to plug into the deserialization pipeline and provide a type to deserialize into based upon the type/namespace that is found in the serialized graph.

Here's a good article on it: http://blogs.msdn.com/b/carlosfigueira/archive/2011/09/21/wcf-extensibility-data-contract-resolver.aspx

IDesign has an implementation of a resolver that can be used for dynamic discovery of types on their site: http://idesign.net/Downloads/GetDownload/1848 (you will probably have to make some modifications to handle generics)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top