Click here to Skip to main content
15,879,535 members
Articles / Programming Languages / C#
Tip/Trick

Serializing Interfaces to a File

Rate me:
Please Sign up or sign in to vote.
4.64/5 (4 votes)
31 Dec 2017CPOL1 min read 8.9K   88   7   2
How to serialize and deserialize implemented interfaces to a file

Introduction

Recently, I have started to look into implementing Inversion of control (IoC) using the Unity framework from Microsoft's patterns and practices. The general IoC is by arranging implementation of classes that is implemented indirectly by using interfaces. This means that if you choose to apply your setting to the registered interface and want to save this for later, it is very practical to be able to serialize the interface. However, this is not implemented out of the box by the .NET Framework.

There is one thing that is a big advantage, and that is that the interfaces are implemented by known types, so all we need to do is grab the types and values that are implemented by the class and save these to a file.

The implementation with its limitations is described in this answer given here and this tip implements the serializer given and adds the deserializer into a project so that it is easily accessible to people. Also, fixed potential memory leak, see the comment below.

Code

The serializer is implemented as was given in the answer as follows:

C#
/// <summary>
/// Creates an XElement from an implemented interface
/// </summary>
/// <param name="o">Object to be serialized</param>
/// <returns>An XElement from the implemented interface</returns>
/// <remarks>see https://stackoverflow.com/questions/1333864/
/// xml-serialization-of-interface-property for original implementation</remarks>
public static XElement Serialize(object ImplementedInterface)
{
    Type ObjectType = ImplementedInterface.GetType();

    Type[] ImplementedTypes = ObjectType.GetProperties()
           .Where(p => p.PropertyType.IsInterface)
           .Select(p => p.GetValue(ImplementedInterface, null).GetType())
           .ToArray();

    DataContractSerializer serializer = new DataContractSerializer(ObjectType, ImplementedTypes);
    using (StringWriter sw = new StringWriter())
    {
        using (XmlTextWriter xw = new XmlTextWriter(sw))
        {
            serializer.WriteObject(xw, ImplementedInterface);
            return XElement.Parse(sw.ToString());
        }
    }                                    
}

and the Deserializer is constructed using the same method:

C#
/// <summary>
/// Creates an object from an interfaces saved in a XElement object
/// </summary>
/// <param name="File">XElement that should be turned into an object</param>
/// <param name="ImplementedInterface">Object that implemented the streamed interface</param>
/// <typeparam name="T">Represents the Interface you want the object casted to</typeparam>
/// <returns></returns>
public static T Deserialize<T>(XElement File, object implementedInterface) 
{
    Type ObjectType = implementedInterface.GetType();
    
    Type[] ImplementedTypes = ObjectType.GetProperties()
        .Where(p => p.PropertyType.IsInterface)
        .Select(p => p.GetValue(implementedInterface, null).GetType())
        .ToArray();
        
    DataContractSerializer serializer = new DataContractSerializer(ObjectType, ImplementedTypes);
    using (StringReader sr = new StringReader(File.ToString()))
    {
        using (XmlTextReader xr = new XmlTextReader(sr))
        {
            return (T)serializer.ReadObject(xr);
        }
    }                          
}

There are some shortcomings of this implementation, and this is if the implementation is of the type IList<IMyItems> or any other implementations where there are nested dependencies of other interface types. These issues can be solved by using reflection.

Usage

The serialization and deserialization can be implemented as:

C#
ISettings MySettings;
// Create a object that implements the interface ISettings
MySettings = new Settings() { Name = "MyFile", ID = 2 };

// Serialize the interface
XElement XMLFile = InterfaceSerializer.Serialize(MySettings);

// You can save the XElement to file using this command:
// XMLFile.Save("SavedXMLFile.XML");

// Change stuff in the original stored value
MySettings.ID = 1;
MySettings.Name = "OldFile";

// Load a saved XElement by using this command:
// XMLFile = XElement.Load("SavedXMLFile.XML");

// Deserialize the object by using the XElement and the types implemented in the original Interface
ISettings OldSettings = InterfaceSerializer.Deserialize<ISettings>(XMLFile,MySettings);

Of all the possible ways to solve the serialization and deserialization of implemented interfaces, I found this method to be the easiest to use in my project.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer
Norway Norway
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionMemory leaks Pin
Stacy Dudovitz1-Jan-18 12:35
professionalStacy Dudovitz1-Jan-18 12:35 
I see you implemented the DataContract serialization answer from the StackOverflow article you referenced. The problem with direct copying answers from StackOverflow w/o review is that it propagates more problems than it solves, and thats how bad code is spread with otherwise clever solutions (admittedly its not your original code that was bad). This leads to comments I often hear from developers like:

"We have to reboot our servers because they leak memory"

And sadly, they are correct... but sadly, its because of incorrect best practices on the part of:.

* The original author
* The person who implemented the answer
* The rest of the team the does not perform code reviews

So, for example, from the StackOverflow article, we find:

public static XElement ToXML(this object o)
{
Type t = o.GetType();

Type[] extraTypes = t.GetProperties()
.Where(p => p.PropertyType.IsInterface)
.Select(p => p.GetValue(o, null).GetType())
.ToArray();

DataContractSerializer serializer = new DataContractSerializer(t, extraTypes);
StringWriter sw = new StringWriter();
XmlTextWriter xw = new XmlTextWriter(sw);
serializer.WriteObject(xw, o);
return XElement.Parse(sw.ToString());
}

where you turned this into your Serialize() method. I went ahead and fixed this to correct for the mis-handling of the IDisposable properties... your method now becomes:

///
/// Creates an XElement from an implemented interface
///

/// <param name="implementedInterface" />
/// <returns>An XElement from the implented interface
/// <remarks>see https://stackoverflow.com/questions/1333864/xml-serialization-of-
/// interface-property for original implementation
public static XElement Serialize(object implementedInterface)
{
var objectType = implementedInterface.GetType();
var implementedTypes = objectType.GetProperties()
.Where(p => p.PropertyType.IsInterface)
.Select(p => p.GetValue(implementedInterface, null).GetType())
.ToArray();

var serializer = new DataContractSerializer(objectType, implementedTypes);

string result;
using (var sw = new StringWriter())
{
using (var xw = new XmlTextWriter(sw))
{
serializer.WriteObject(xw, implementedInterface);
result = sw.ToString();
}
}

return XElement.Parse(result);
}

Everything is now scoped within "using { }'s", and your helper methods will no longer leak.

If we were really to find the original source of the problem, its Microsoft, who have done a piss-poor job of explaining the proper technique of handling garbage collection and best practices for memory management... and the problem does not go away with .NET Core... <sigh>
AnswerRe: Memory leaks Pin
Kenneth Haugland1-Jan-18 19:18
mvaKenneth Haugland1-Jan-18 19:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.