Thursday, May 10, 2007

.NET Interface Serialization Issue

Interface can not be serialized or de-serialized. That's because interface is just a contract not detailed implementation. However, in some cases, we do want to expose interface so objects are not restricted by a specific type.

Look at a simple example:
    public interface IPriceable
{
double GetPrice();
}
[Serializable]
public class ProductA : IPriceable
{
public double GetPrice()
{
return 123.345;
}
}

[Serializable]
public class ProductB : IPriceable
{
public double GetPrice()
{
return 234.567;
}
}

[Serializable]
public class PromotionPackage
{
public List<IPriceable> Items = new List<IPriceable>();
double GetPromotePrice()
{
//return PromotionCaculator.GetPrice(_items);
return 567.89; ;
}
}
The PromotionPackage contains a list of IPriceable objects (ProductA, ProductB etc.). Everything is okay except that an exception will be thrown when we serialize the PromotionPackage object:
{"Cannot serialize member PromotionPackage.Items of type IPriceable because it is an interface."}
A workaround would be wrapping the interface to an abstract class:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

public class Program
{
public interface IPriceable
{
double GetPrice();
}

[XmlInclude(typeof(ProductA))]
[XmlInclude(typeof(ProductB))]
public abstract class ProductBase : IPriceable
{
public abstract double GetPrice();
}

[Serializable]
public class ProductA : ProductBase
{
public override double GetPrice()
{
return 123.345;
}
}

[Serializable]
public class ProductB : ProductBase
{
public override double GetPrice()
{
return 234.567;
}
}

[Serializable]
public class PromotionPackage
{
public List<ProductBase> Items = new List<ProductBase>();
double GetPromotePrice()
{
//return PromotionCaculator.GetPrice(_items);
return 567.89; ;
}
}

static void Main(string[] args)
{
PromotionPackage package = new PromotionPackage();
package.Items.Add(new ProductA());
package.Items.Add(new ProductB());

string xmlString = string.Empty;
XmlSerializer serializer = new XmlSerializer(typeof(PromotionPackage));

// Serialize
using (StringWriter sw = new StringWriter())
{
serializer.Serialize(sw, package);
xmlString = sw.ToString();
}

// De-serialize
PromotionPackage newPackage = null;
using (StringReader sr = new StringReader(xmlString))
{
XmlTextReader reader = new XmlTextReader(sr);
newPackage = (PromotionPackage)serializer.Deserialize(reader);
}

Console.WriteLine("Done.");
Console.Read();
}
}