How to serialize List<IFilter> (Nokia Imaging SDK)?

104 Views Asked by At

I'm trying to save a List of IFilter(of type Interface) which are applied to an image using XML serialization, so that user can edit the same image from where he left off.

[XmlRoot]
public class ImageProperties
{
    public string ImageName { get; set; }
    public string ImageFilePath { get; set; }
    public List<IFilter> Filters { get; set; }
}

Is this possible? Is there another alternative to do the same?

2

There are 2 best solutions below

2
On BEST ANSWER

You could use IXmlSerializable to achieve this.. assuming you can change the ImageProperties class.

Upon serialization, you can get the type by looking at each filter instance and querying for it. You can store this type information in the XML so when you come to read it, you know which type it is, and you can then just invoke the default XML serializer for each filter.

Here is a possible implementation.

public class ImageProperties : IXmlSerializable
{
    public string ImageName { get; set; }
    public string ImageFilePath { get; set; }
    public List<IFilter> Filters { get; set; }

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        string startEle = reader.Name;            
        reader.ReadStartElement();
        Filters = new List<IFilter>();

        do
        {
            switch (reader.Name)
            {
                case "imgName":
                    ImageName = reader.ReadElementContentAsString();
                    break;
                case "imgFilePath":
                    ImageFilePath = reader.ReadElementContentAsString();
                    break;
                case "filters":
                    reader.ReadStartElement("filters");
                    while (reader.Name.Equals("iFilter"))
                    {
                        XmlSerializer filterSerializer = new XmlSerializer(Type.GetType(reader.GetAttribute("type")));
                        reader.ReadStartElement("iFilter");
                        Filters.Add((IFilter)filterSerializer.Deserialize(reader));
                        reader.ReadEndElement();
                    }
                    reader.ReadEndElement();
                    break; 

                default:
                    reader.ReadOuterXml();
                    break;                  
            }

        } while (reader.Name != startEle);
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        writer.WriteElementString("imgName", ImageName);
        writer.WriteElementString("imgFilePath", ImageFilePath);
        writer.WriteStartElement("filters");            
        foreach (IFilter filter in Filters)
        {
            writer.WriteStartElement("iFilter");
            writer.WriteAttributeString("type", filter.GetType().FullName);
            XmlSerializer filterSerializer = new XmlSerializer(filter.GetType());
            filterSerializer.Serialize(writer, filter);
            writer.WriteEndElement();
        }
        writer.WriteEndElement();
    }
}

If you have different types of filter, the default serializer of the real type is invoked, so their unique properties will get recorded.

For example, with these filter classes available:

public interface IFilter
{
    string SomeCommonProp { get; set;}    
}

[XmlRoot("myFilter")]
public class MyFilter : IFilter
{

    [XmlElement("somemyFilterProp")]
    public string SomeMyFilterProp { get; set; }

    [XmlElement("someCommonProp")]
    public string SomeCommonProp { get; set;}
}

[XmlRoot("myOtherFilter")]
public class MyOtherFilter : IFilter
{
    [XmlElement("someOtherFilterProp")]
    public string SomeOtherFilterProp { get; set; }

    [XmlElement("someCommonProp")]
    public string SomeCommonProp { get; set;}
}

You can use as follows to serialise and deserialise two different types of filter in IFilters to xml.

static void Main(string[] args)
{
    ImageProperties props = new ImageProperties();
    props.ImageName = "img.png";
    props.ImageFilePath = "c:\\temp\\img.png";
    props.Filters = new List<IFilter>();
    props.Filters.Add(new MyFilter() { SomeMyFilterProp = "x", SomeCommonProp ="p" });
    props.Filters.Add(new MyOtherFilter() { SomeOtherFilterProp = "y", SomeCommonProp ="p" });

    XmlSerializer s = new XmlSerializer(typeof(ImageProperties));
    using (StreamWriter writer = new StreamWriter(@"c:\temp\imgprops.xml"))
        s.Serialize(writer, props);

    using (StreamReader reader = new StreamReader(@"c:\temp\imgprops.xml"))
    {
        object andBack = s.Deserialize(reader);
    }

    Console.ReadKey();
}

This produces an XML that looks like this.

<?xml version="1.0" encoding="utf-8"?>
<ImageProperties>
  <imgName>img.png</imgName>
  <imgFilePath>c:\temp\img.png</imgFilePath>
  <filters>
    <iFilter type="SomeNameSpace.Whatever.MyFilter">
      <myFilter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <somemyFilterProp>x</somemyFilterProp>
        <someCommonProp>p</someCommonProp>
      </myFilter>
    </iFilter>
    <iFilter type="SomeNameSpace.Whatever.MyOtherFilter">
      <myOtherFilter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <someOtherFilterProp>y</someOtherFilterProp>
        <someCommonProp>p</someCommonProp>
      </myOtherFilter>
    </iFilter>
  </filters>
</ImageProperties>
0
On

No. Interface instances cannot be serialized. It doesn't know the implementation to "deserialize" to. It will require a concrete class or custom serialization in this case.