Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serializing and restoring an unknown class

A base project contains an abstract base class Foo. In separate client projects, there are classes implementing that base class.

I'd like to serialize and restore an instance of a concrete class by calling some method on the base class:

// In the base project:
public abstract class Foo
{
    abstract void Save (string path);
    abstract Foo Load (string path);
}

It can be assumed that at the time of deserialization, all needed classes are present. If possible in any way, the serialization should be done in XML. Making the base class implement IXmlSerializable is possible.

I'm a bit stuck here. If my understanding of things is correct, then this is only possible by adding an [XmlInclude(typeof(UnknownClass))] to the base class for every implementing class - but the implementing classes are unknown!

Is there a way to do this? I've got no experience with reflection, but i also welcome answers using it.

Edit: The problem is Deserializing. Just serializing would be kind of easy. :-)

like image 929
mafu Avatar asked Feb 26 '09 14:02

mafu


2 Answers

You can also do this at the point of creating an XmlSerializer, by providing the additional details in the constructor. Note that it doesn't re-use such models, so you'd want to configure the XmlSerializer once (at app startup, from configuration), and re-use it repeatedly... note many more customizations are possible with the XmlAttributeOverrides overload...

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
static class Program
{
    static readonly XmlSerializer ser;
    static Program()
    {
        List<Type> extraTypes = new List<Type>();
        // TODO: read config, or use reflection to
        // look at all assemblies
        extraTypes.Add(typeof(Bar));
        ser = new XmlSerializer(typeof(Foo), extraTypes.ToArray());
    }
    static void Main()
    {
        Foo foo = new Bar();
        MemoryStream ms = new MemoryStream();
        ser.Serialize(ms, foo);
        ms.Position = 0;
        Foo clone = (Foo)ser.Deserialize(ms);
        Console.WriteLine(clone.GetType());
    }
}

public abstract class Foo { }
public class Bar : Foo {}
like image 68
Marc Gravell Avatar answered Sep 21 '22 01:09

Marc Gravell


You don't have to put the serialization functions into any base class, instead, you can add it to your Utility Class.

e.g. ( the code is for example only, rootName is optional )

public static class Utility
{
       public static void ToXml<T>(T src, string rootName, string fileName) where T : class, new()
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T), new XmlRootAttribute(rootName));
            XmlTextWriter writer = new XmlTextWriter(fileName, Encoding.UTF8);
            serializer.Serialize(writer, src);
            writer.Flush();
            writer.Close();
        }
}

Simply make call to

Utility.ToXml( fooObj, "Foo", @"c:\foo.xml");

Not only Foo's family types can use it, but all other serializable objects.

EDIT

OK full service... (rootName is optional)

public static T FromXml<T>(T src, string rootName, string fileName) where T : class, new()
{
    XmlSerializer serializer = new XmlSerializer(typeof(T), new XmlRootAttribute(rootName));
    TextReader reader = new StreamReader(fileName);
    return serializer.Deserialize(reader) as T;
}
like image 40
Ray Lu Avatar answered Sep 21 '22 01:09

Ray Lu